OSDN Git Service

[Develop and manual test] /api/v1/commandList
[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
64
65 /***/ },
66 /* 1 */
67 /***/ function(module, exports, __webpack_require__) {
68
69         __webpack_require__(2);
70         module.exports = angular;
71
72
73 /***/ },
74 /* 2 */
75 /***/ function(module, exports) {
76
77         /**
78          * @license AngularJS v1.4.8
79          * (c) 2010-2015 Google, Inc. http://angularjs.org
80          * License: MIT
81          */
82         (function(window, document, undefined) {'use strict';
83
84         /**
85          * @description
86          *
87          * This object provides a utility for producing rich Error messages within
88          * Angular. It can be called as follows:
89          *
90          * var exampleMinErr = minErr('example');
91          * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
92          *
93          * The above creates an instance of minErr in the example namespace. The
94          * resulting error will have a namespaced error code of example.one.  The
95          * resulting error will replace {0} with the value of foo, and {1} with the
96          * value of bar. The object is not restricted in the number of arguments it can
97          * take.
98          *
99          * If fewer arguments are specified than necessary for interpolation, the extra
100          * interpolation markers will be preserved in the final string.
101          *
102          * Since data will be parsed statically during a build step, some restrictions
103          * are applied with respect to how minErr instances are created and called.
104          * Instances should have names of the form namespaceMinErr for a minErr created
105          * using minErr('namespace') . Error codes, namespaces and template strings
106          * should all be static strings, not variables or general expressions.
107          *
108          * @param {string} module The namespace to use for the new minErr instance.
109          * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
110          *   error from returned function, for cases when a particular type of error is useful.
111          * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
112          */
113
114         function minErr(module, ErrorConstructor) {
115           ErrorConstructor = ErrorConstructor || Error;
116           return function() {
117             var SKIP_INDEXES = 2;
118
119             var templateArgs = arguments,
120               code = templateArgs[0],
121               message = '[' + (module ? module + ':' : '') + code + '] ',
122               template = templateArgs[1],
123               paramPrefix, i;
124
125             message += template.replace(/\{\d+\}/g, function(match) {
126               var index = +match.slice(1, -1),
127                 shiftedIndex = index + SKIP_INDEXES;
128
129               if (shiftedIndex < templateArgs.length) {
130                 return toDebugString(templateArgs[shiftedIndex]);
131               }
132
133               return match;
134             });
135
136             message += '\nhttp://errors.angularjs.org/1.4.8/' +
137               (module ? module + '/' : '') + code;
138
139             for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
140               message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
141                 encodeURIComponent(toDebugString(templateArgs[i]));
142             }
143
144             return new ErrorConstructor(message);
145           };
146         }
147
148         /* We need to tell jshint what variables are being exported */
149         /* global angular: true,
150           msie: true,
151           jqLite: true,
152           jQuery: true,
153           slice: true,
154           splice: true,
155           push: true,
156           toString: true,
157           ngMinErr: true,
158           angularModule: true,
159           uid: true,
160           REGEX_STRING_REGEXP: true,
161           VALIDITY_STATE_PROPERTY: true,
162
163           lowercase: true,
164           uppercase: true,
165           manualLowercase: true,
166           manualUppercase: true,
167           nodeName_: true,
168           isArrayLike: true,
169           forEach: true,
170           forEachSorted: true,
171           reverseParams: true,
172           nextUid: true,
173           setHashKey: true,
174           extend: true,
175           toInt: true,
176           inherit: true,
177           merge: true,
178           noop: true,
179           identity: true,
180           valueFn: true,
181           isUndefined: true,
182           isDefined: true,
183           isObject: true,
184           isBlankObject: true,
185           isString: true,
186           isNumber: true,
187           isDate: true,
188           isArray: true,
189           isFunction: true,
190           isRegExp: true,
191           isWindow: true,
192           isScope: true,
193           isFile: true,
194           isFormData: true,
195           isBlob: true,
196           isBoolean: true,
197           isPromiseLike: true,
198           trim: true,
199           escapeForRegexp: true,
200           isElement: true,
201           makeMap: true,
202           includes: true,
203           arrayRemove: true,
204           copy: true,
205           shallowCopy: true,
206           equals: true,
207           csp: true,
208           jq: true,
209           concat: true,
210           sliceArgs: true,
211           bind: true,
212           toJsonReplacer: true,
213           toJson: true,
214           fromJson: true,
215           convertTimezoneToLocal: true,
216           timezoneToOffset: true,
217           startingTag: true,
218           tryDecodeURIComponent: true,
219           parseKeyValue: true,
220           toKeyValue: true,
221           encodeUriSegment: true,
222           encodeUriQuery: true,
223           angularInit: true,
224           bootstrap: true,
225           getTestability: true,
226           snake_case: true,
227           bindJQuery: true,
228           assertArg: true,
229           assertArgFn: true,
230           assertNotHasOwnProperty: true,
231           getter: true,
232           getBlockNodes: true,
233           hasOwnProperty: true,
234           createMap: true,
235
236           NODE_TYPE_ELEMENT: true,
237           NODE_TYPE_ATTRIBUTE: true,
238           NODE_TYPE_TEXT: true,
239           NODE_TYPE_COMMENT: true,
240           NODE_TYPE_DOCUMENT: true,
241           NODE_TYPE_DOCUMENT_FRAGMENT: true,
242         */
243
244         ////////////////////////////////////
245
246         /**
247          * @ngdoc module
248          * @name ng
249          * @module ng
250          * @description
251          *
252          * # ng (core module)
253          * The ng module is loaded by default when an AngularJS application is started. The module itself
254          * contains the essential components for an AngularJS application to function. The table below
255          * lists a high level breakdown of each of the services/factories, filters, directives and testing
256          * components available within this core module.
257          *
258          * <div doc-module-components="ng"></div>
259          */
260
261         var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
262
263         // The name of a form control's ValidityState property.
264         // This is used so that it's possible for internal tests to create mock ValidityStates.
265         var VALIDITY_STATE_PROPERTY = 'validity';
266
267         /**
268          * @ngdoc function
269          * @name angular.lowercase
270          * @module ng
271          * @kind function
272          *
273          * @description Converts the specified string to lowercase.
274          * @param {string} string String to be converted to lowercase.
275          * @returns {string} Lowercased string.
276          */
277         var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
278         var hasOwnProperty = Object.prototype.hasOwnProperty;
279
280         /**
281          * @ngdoc function
282          * @name angular.uppercase
283          * @module ng
284          * @kind function
285          *
286          * @description Converts the specified string to uppercase.
287          * @param {string} string String to be converted to uppercase.
288          * @returns {string} Uppercased string.
289          */
290         var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
291
292
293         var manualLowercase = function(s) {
294           /* jshint bitwise: false */
295           return isString(s)
296               ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
297               : s;
298         };
299         var manualUppercase = function(s) {
300           /* jshint bitwise: false */
301           return isString(s)
302               ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
303               : s;
304         };
305
306
307         // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
308         // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
309         // with correct but slower alternatives.
310         if ('i' !== 'I'.toLowerCase()) {
311           lowercase = manualLowercase;
312           uppercase = manualUppercase;
313         }
314
315
316         var
317             msie,             // holds major version number for IE, or NaN if UA is not IE.
318             jqLite,           // delay binding since jQuery could be loaded after us.
319             jQuery,           // delay binding
320             slice             = [].slice,
321             splice            = [].splice,
322             push              = [].push,
323             toString          = Object.prototype.toString,
324             getPrototypeOf    = Object.getPrototypeOf,
325             ngMinErr          = minErr('ng'),
326
327             /** @name angular */
328             angular           = window.angular || (window.angular = {}),
329             angularModule,
330             uid               = 0;
331
332         /**
333          * documentMode is an IE-only property
334          * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
335          */
336         msie = document.documentMode;
337
338
339         /**
340          * @private
341          * @param {*} obj
342          * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
343          *                   String ...)
344          */
345         function isArrayLike(obj) {
346
347           // `null`, `undefined` and `window` are not array-like
348           if (obj == null || isWindow(obj)) return false;
349
350           // arrays, strings and jQuery/jqLite objects are array like
351           // * jqLite is either the jQuery or jqLite constructor function
352           // * we have to check the existance of jqLite first as this method is called
353           //   via the forEach method when constructing the jqLite object in the first place
354           if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
355
356           // Support: iOS 8.2 (not reproducible in simulator)
357           // "length" in obj used to prevent JIT error (gh-11508)
358           var length = "length" in Object(obj) && obj.length;
359
360           // NodeList objects (with `item` method) and
361           // other objects with suitable length characteristics are array-like
362           return isNumber(length) &&
363             (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
364         }
365
366         /**
367          * @ngdoc function
368          * @name angular.forEach
369          * @module ng
370          * @kind function
371          *
372          * @description
373          * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
374          * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
375          * is the value of an object property or an array element, `key` is the object property key or
376          * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
377          *
378          * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
379          * using the `hasOwnProperty` method.
380          *
381          * Unlike ES262's
382          * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
383          * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
384          * return the value provided.
385          *
386            ```js
387              var values = {name: 'misko', gender: 'male'};
388              var log = [];
389              angular.forEach(values, function(value, key) {
390                this.push(key + ': ' + value);
391              }, log);
392              expect(log).toEqual(['name: misko', 'gender: male']);
393            ```
394          *
395          * @param {Object|Array} obj Object to iterate over.
396          * @param {Function} iterator Iterator function.
397          * @param {Object=} context Object to become context (`this`) for the iterator function.
398          * @returns {Object|Array} Reference to `obj`.
399          */
400
401         function forEach(obj, iterator, context) {
402           var key, length;
403           if (obj) {
404             if (isFunction(obj)) {
405               for (key in obj) {
406                 // Need to check if hasOwnProperty exists,
407                 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
408                 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
409                   iterator.call(context, obj[key], key, obj);
410                 }
411               }
412             } else if (isArray(obj) || isArrayLike(obj)) {
413               var isPrimitive = typeof obj !== 'object';
414               for (key = 0, length = obj.length; key < length; key++) {
415                 if (isPrimitive || key in obj) {
416                   iterator.call(context, obj[key], key, obj);
417                 }
418               }
419             } else if (obj.forEach && obj.forEach !== forEach) {
420                 obj.forEach(iterator, context, obj);
421             } else if (isBlankObject(obj)) {
422               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
423               for (key in obj) {
424                 iterator.call(context, obj[key], key, obj);
425               }
426             } else if (typeof obj.hasOwnProperty === 'function') {
427               // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
428               for (key in obj) {
429                 if (obj.hasOwnProperty(key)) {
430                   iterator.call(context, obj[key], key, obj);
431                 }
432               }
433             } else {
434               // Slow path for objects which do not have a method `hasOwnProperty`
435               for (key in obj) {
436                 if (hasOwnProperty.call(obj, key)) {
437                   iterator.call(context, obj[key], key, obj);
438                 }
439               }
440             }
441           }
442           return obj;
443         }
444
445         function forEachSorted(obj, iterator, context) {
446           var keys = Object.keys(obj).sort();
447           for (var i = 0; i < keys.length; i++) {
448             iterator.call(context, obj[keys[i]], keys[i]);
449           }
450           return keys;
451         }
452
453
454         /**
455          * when using forEach the params are value, key, but it is often useful to have key, value.
456          * @param {function(string, *)} iteratorFn
457          * @returns {function(*, string)}
458          */
459         function reverseParams(iteratorFn) {
460           return function(value, key) { iteratorFn(key, value); };
461         }
462
463         /**
464          * A consistent way of creating unique IDs in angular.
465          *
466          * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
467          * we hit number precision issues in JavaScript.
468          *
469          * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
470          *
471          * @returns {number} an unique alpha-numeric string
472          */
473         function nextUid() {
474           return ++uid;
475         }
476
477
478         /**
479          * Set or clear the hashkey for an object.
480          * @param obj object
481          * @param h the hashkey (!truthy to delete the hashkey)
482          */
483         function setHashKey(obj, h) {
484           if (h) {
485             obj.$$hashKey = h;
486           } else {
487             delete obj.$$hashKey;
488           }
489         }
490
491
492         function baseExtend(dst, objs, deep) {
493           var h = dst.$$hashKey;
494
495           for (var i = 0, ii = objs.length; i < ii; ++i) {
496             var obj = objs[i];
497             if (!isObject(obj) && !isFunction(obj)) continue;
498             var keys = Object.keys(obj);
499             for (var j = 0, jj = keys.length; j < jj; j++) {
500               var key = keys[j];
501               var src = obj[key];
502
503               if (deep && isObject(src)) {
504                 if (isDate(src)) {
505                   dst[key] = new Date(src.valueOf());
506                 } else if (isRegExp(src)) {
507                   dst[key] = new RegExp(src);
508                 } else if (src.nodeName) {
509                   dst[key] = src.cloneNode(true);
510                 } else if (isElement(src)) {
511                   dst[key] = src.clone();
512                 } else {
513                   if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
514                   baseExtend(dst[key], [src], true);
515                 }
516               } else {
517                 dst[key] = src;
518               }
519             }
520           }
521
522           setHashKey(dst, h);
523           return dst;
524         }
525
526         /**
527          * @ngdoc function
528          * @name angular.extend
529          * @module ng
530          * @kind function
531          *
532          * @description
533          * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
534          * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
535          * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
536          *
537          * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
538          * {@link angular.merge} for this.
539          *
540          * @param {Object} dst Destination object.
541          * @param {...Object} src Source object(s).
542          * @returns {Object} Reference to `dst`.
543          */
544         function extend(dst) {
545           return baseExtend(dst, slice.call(arguments, 1), false);
546         }
547
548
549         /**
550         * @ngdoc function
551         * @name angular.merge
552         * @module ng
553         * @kind function
554         *
555         * @description
556         * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
557         * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
558         * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
559         *
560         * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
561         * objects, performing a deep copy.
562         *
563         * @param {Object} dst Destination object.
564         * @param {...Object} src Source object(s).
565         * @returns {Object} Reference to `dst`.
566         */
567         function merge(dst) {
568           return baseExtend(dst, slice.call(arguments, 1), true);
569         }
570
571
572
573         function toInt(str) {
574           return parseInt(str, 10);
575         }
576
577
578         function inherit(parent, extra) {
579           return extend(Object.create(parent), extra);
580         }
581
582         /**
583          * @ngdoc function
584          * @name angular.noop
585          * @module ng
586          * @kind function
587          *
588          * @description
589          * A function that performs no operations. This function can be useful when writing code in the
590          * functional style.
591            ```js
592              function foo(callback) {
593                var result = calculateResult();
594                (callback || angular.noop)(result);
595              }
596            ```
597          */
598         function noop() {}
599         noop.$inject = [];
600
601
602         /**
603          * @ngdoc function
604          * @name angular.identity
605          * @module ng
606          * @kind function
607          *
608          * @description
609          * A function that returns its first argument. This function is useful when writing code in the
610          * functional style.
611          *
612            ```js
613              function transformer(transformationFn, value) {
614                return (transformationFn || angular.identity)(value);
615              };
616            ```
617           * @param {*} value to be returned.
618           * @returns {*} the value passed in.
619          */
620         function identity($) {return $;}
621         identity.$inject = [];
622
623
624         function valueFn(value) {return function() {return value;};}
625
626         function hasCustomToString(obj) {
627           return isFunction(obj.toString) && obj.toString !== toString;
628         }
629
630
631         /**
632          * @ngdoc function
633          * @name angular.isUndefined
634          * @module ng
635          * @kind function
636          *
637          * @description
638          * Determines if a reference is undefined.
639          *
640          * @param {*} value Reference to check.
641          * @returns {boolean} True if `value` is undefined.
642          */
643         function isUndefined(value) {return typeof value === 'undefined';}
644
645
646         /**
647          * @ngdoc function
648          * @name angular.isDefined
649          * @module ng
650          * @kind function
651          *
652          * @description
653          * Determines if a reference is defined.
654          *
655          * @param {*} value Reference to check.
656          * @returns {boolean} True if `value` is defined.
657          */
658         function isDefined(value) {return typeof value !== 'undefined';}
659
660
661         /**
662          * @ngdoc function
663          * @name angular.isObject
664          * @module ng
665          * @kind function
666          *
667          * @description
668          * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
669          * considered to be objects. Note that JavaScript arrays are objects.
670          *
671          * @param {*} value Reference to check.
672          * @returns {boolean} True if `value` is an `Object` but not `null`.
673          */
674         function isObject(value) {
675           // http://jsperf.com/isobject4
676           return value !== null && typeof value === 'object';
677         }
678
679
680         /**
681          * Determine if a value is an object with a null prototype
682          *
683          * @returns {boolean} True if `value` is an `Object` with a null prototype
684          */
685         function isBlankObject(value) {
686           return value !== null && typeof value === 'object' && !getPrototypeOf(value);
687         }
688
689
690         /**
691          * @ngdoc function
692          * @name angular.isString
693          * @module ng
694          * @kind function
695          *
696          * @description
697          * Determines if a reference is a `String`.
698          *
699          * @param {*} value Reference to check.
700          * @returns {boolean} True if `value` is a `String`.
701          */
702         function isString(value) {return typeof value === 'string';}
703
704
705         /**
706          * @ngdoc function
707          * @name angular.isNumber
708          * @module ng
709          * @kind function
710          *
711          * @description
712          * Determines if a reference is a `Number`.
713          *
714          * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
715          *
716          * If you wish to exclude these then you can use the native
717          * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
718          * method.
719          *
720          * @param {*} value Reference to check.
721          * @returns {boolean} True if `value` is a `Number`.
722          */
723         function isNumber(value) {return typeof value === 'number';}
724
725
726         /**
727          * @ngdoc function
728          * @name angular.isDate
729          * @module ng
730          * @kind function
731          *
732          * @description
733          * Determines if a value is a date.
734          *
735          * @param {*} value Reference to check.
736          * @returns {boolean} True if `value` is a `Date`.
737          */
738         function isDate(value) {
739           return toString.call(value) === '[object Date]';
740         }
741
742
743         /**
744          * @ngdoc function
745          * @name angular.isArray
746          * @module ng
747          * @kind function
748          *
749          * @description
750          * Determines if a reference is an `Array`.
751          *
752          * @param {*} value Reference to check.
753          * @returns {boolean} True if `value` is an `Array`.
754          */
755         var isArray = Array.isArray;
756
757         /**
758          * @ngdoc function
759          * @name angular.isFunction
760          * @module ng
761          * @kind function
762          *
763          * @description
764          * Determines if a reference is a `Function`.
765          *
766          * @param {*} value Reference to check.
767          * @returns {boolean} True if `value` is a `Function`.
768          */
769         function isFunction(value) {return typeof value === 'function';}
770
771
772         /**
773          * Determines if a value is a regular expression object.
774          *
775          * @private
776          * @param {*} value Reference to check.
777          * @returns {boolean} True if `value` is a `RegExp`.
778          */
779         function isRegExp(value) {
780           return toString.call(value) === '[object RegExp]';
781         }
782
783
784         /**
785          * Checks if `obj` is a window object.
786          *
787          * @private
788          * @param {*} obj Object to check
789          * @returns {boolean} True if `obj` is a window obj.
790          */
791         function isWindow(obj) {
792           return obj && obj.window === obj;
793         }
794
795
796         function isScope(obj) {
797           return obj && obj.$evalAsync && obj.$watch;
798         }
799
800
801         function isFile(obj) {
802           return toString.call(obj) === '[object File]';
803         }
804
805
806         function isFormData(obj) {
807           return toString.call(obj) === '[object FormData]';
808         }
809
810
811         function isBlob(obj) {
812           return toString.call(obj) === '[object Blob]';
813         }
814
815
816         function isBoolean(value) {
817           return typeof value === 'boolean';
818         }
819
820
821         function isPromiseLike(obj) {
822           return obj && isFunction(obj.then);
823         }
824
825
826         var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
827         function isTypedArray(value) {
828           return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
829         }
830
831
832         var trim = function(value) {
833           return isString(value) ? value.trim() : value;
834         };
835
836         // Copied from:
837         // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
838         // Prereq: s is a string.
839         var escapeForRegexp = function(s) {
840           return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
841                    replace(/\x08/g, '\\x08');
842         };
843
844
845         /**
846          * @ngdoc function
847          * @name angular.isElement
848          * @module ng
849          * @kind function
850          *
851          * @description
852          * Determines if a reference is a DOM element (or wrapped jQuery element).
853          *
854          * @param {*} value Reference to check.
855          * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
856          */
857         function isElement(node) {
858           return !!(node &&
859             (node.nodeName  // we are a direct element
860             || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
861         }
862
863         /**
864          * @param str 'key1,key2,...'
865          * @returns {object} in the form of {key1:true, key2:true, ...}
866          */
867         function makeMap(str) {
868           var obj = {}, items = str.split(","), i;
869           for (i = 0; i < items.length; i++) {
870             obj[items[i]] = true;
871           }
872           return obj;
873         }
874
875
876         function nodeName_(element) {
877           return lowercase(element.nodeName || (element[0] && element[0].nodeName));
878         }
879
880         function includes(array, obj) {
881           return Array.prototype.indexOf.call(array, obj) != -1;
882         }
883
884         function arrayRemove(array, value) {
885           var index = array.indexOf(value);
886           if (index >= 0) {
887             array.splice(index, 1);
888           }
889           return index;
890         }
891
892         /**
893          * @ngdoc function
894          * @name angular.copy
895          * @module ng
896          * @kind function
897          *
898          * @description
899          * Creates a deep copy of `source`, which should be an object or an array.
900          *
901          * * If no destination is supplied, a copy of the object or array is created.
902          * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
903          *   are deleted and then all elements/properties from the source are copied to it.
904          * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
905          * * If `source` is identical to 'destination' an exception will be thrown.
906          *
907          * @param {*} source The source that will be used to make a copy.
908          *                   Can be any type, including primitives, `null`, and `undefined`.
909          * @param {(Object|Array)=} destination Destination into which the source is copied. If
910          *     provided, must be of the same type as `source`.
911          * @returns {*} The copy or updated `destination`, if `destination` was specified.
912          *
913          * @example
914          <example module="copyExample">
915          <file name="index.html">
916          <div ng-controller="ExampleController">
917          <form novalidate class="simple-form">
918          Name: <input type="text" ng-model="user.name" /><br />
919          E-mail: <input type="email" ng-model="user.email" /><br />
920          Gender: <input type="radio" ng-model="user.gender" value="male" />male
921          <input type="radio" ng-model="user.gender" value="female" />female<br />
922          <button ng-click="reset()">RESET</button>
923          <button ng-click="update(user)">SAVE</button>
924          </form>
925          <pre>form = {{user | json}}</pre>
926          <pre>master = {{master | json}}</pre>
927          </div>
928
929          <script>
930           angular.module('copyExample', [])
931             .controller('ExampleController', ['$scope', function($scope) {
932               $scope.master= {};
933
934               $scope.update = function(user) {
935                 // Example with 1 argument
936                 $scope.master= angular.copy(user);
937               };
938
939               $scope.reset = function() {
940                 // Example with 2 arguments
941                 angular.copy($scope.master, $scope.user);
942               };
943
944               $scope.reset();
945             }]);
946          </script>
947          </file>
948          </example>
949          */
950         function copy(source, destination) {
951           var stackSource = [];
952           var stackDest = [];
953
954           if (destination) {
955             if (isTypedArray(destination)) {
956               throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
957             }
958             if (source === destination) {
959               throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
960             }
961
962             // Empty the destination object
963             if (isArray(destination)) {
964               destination.length = 0;
965             } else {
966               forEach(destination, function(value, key) {
967                 if (key !== '$$hashKey') {
968                   delete destination[key];
969                 }
970               });
971             }
972
973             stackSource.push(source);
974             stackDest.push(destination);
975             return copyRecurse(source, destination);
976           }
977
978           return copyElement(source);
979
980           function copyRecurse(source, destination) {
981             var h = destination.$$hashKey;
982             var result, key;
983             if (isArray(source)) {
984               for (var i = 0, ii = source.length; i < ii; i++) {
985                 destination.push(copyElement(source[i]));
986               }
987             } else if (isBlankObject(source)) {
988               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
989               for (key in source) {
990                 destination[key] = copyElement(source[key]);
991               }
992             } else if (source && typeof source.hasOwnProperty === 'function') {
993               // Slow path, which must rely on hasOwnProperty
994               for (key in source) {
995                 if (source.hasOwnProperty(key)) {
996                   destination[key] = copyElement(source[key]);
997                 }
998               }
999             } else {
1000               // Slowest path --- hasOwnProperty can't be called as a method
1001               for (key in source) {
1002                 if (hasOwnProperty.call(source, key)) {
1003                   destination[key] = copyElement(source[key]);
1004                 }
1005               }
1006             }
1007             setHashKey(destination, h);
1008             return destination;
1009           }
1010
1011           function copyElement(source) {
1012             // Simple values
1013             if (!isObject(source)) {
1014               return source;
1015             }
1016
1017             // Already copied values
1018             var index = stackSource.indexOf(source);
1019             if (index !== -1) {
1020               return stackDest[index];
1021             }
1022
1023             if (isWindow(source) || isScope(source)) {
1024               throw ngMinErr('cpws',
1025                 "Can't copy! Making copies of Window or Scope instances is not supported.");
1026             }
1027
1028             var needsRecurse = false;
1029             var destination;
1030
1031             if (isArray(source)) {
1032               destination = [];
1033               needsRecurse = true;
1034             } else if (isTypedArray(source)) {
1035               destination = new source.constructor(source);
1036             } else if (isDate(source)) {
1037               destination = new Date(source.getTime());
1038             } else if (isRegExp(source)) {
1039               destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1040               destination.lastIndex = source.lastIndex;
1041             } else if (isFunction(source.cloneNode)) {
1042                 destination = source.cloneNode(true);
1043             } else {
1044               destination = Object.create(getPrototypeOf(source));
1045               needsRecurse = true;
1046             }
1047
1048             stackSource.push(source);
1049             stackDest.push(destination);
1050
1051             return needsRecurse
1052               ? copyRecurse(source, destination)
1053               : destination;
1054           }
1055         }
1056
1057         /**
1058          * Creates a shallow copy of an object, an array or a primitive.
1059          *
1060          * Assumes that there are no proto properties for objects.
1061          */
1062         function shallowCopy(src, dst) {
1063           if (isArray(src)) {
1064             dst = dst || [];
1065
1066             for (var i = 0, ii = src.length; i < ii; i++) {
1067               dst[i] = src[i];
1068             }
1069           } else if (isObject(src)) {
1070             dst = dst || {};
1071
1072             for (var key in src) {
1073               if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1074                 dst[key] = src[key];
1075               }
1076             }
1077           }
1078
1079           return dst || src;
1080         }
1081
1082
1083         /**
1084          * @ngdoc function
1085          * @name angular.equals
1086          * @module ng
1087          * @kind function
1088          *
1089          * @description
1090          * Determines if two objects or two values are equivalent. Supports value types, regular
1091          * expressions, arrays and objects.
1092          *
1093          * Two objects or values are considered equivalent if at least one of the following is true:
1094          *
1095          * * Both objects or values pass `===` comparison.
1096          * * Both objects or values are of the same type and all of their properties are equal by
1097          *   comparing them with `angular.equals`.
1098          * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1099          * * Both values represent the same regular expression (In JavaScript,
1100          *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1101          *   representation matches).
1102          *
1103          * During a property comparison, properties of `function` type and properties with names
1104          * that begin with `$` are ignored.
1105          *
1106          * Scope and DOMWindow objects are being compared only by identify (`===`).
1107          *
1108          * @param {*} o1 Object or value to compare.
1109          * @param {*} o2 Object or value to compare.
1110          * @returns {boolean} True if arguments are equal.
1111          */
1112         function equals(o1, o2) {
1113           if (o1 === o2) return true;
1114           if (o1 === null || o2 === null) return false;
1115           if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1116           var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1117           if (t1 == t2) {
1118             if (t1 == 'object') {
1119               if (isArray(o1)) {
1120                 if (!isArray(o2)) return false;
1121                 if ((length = o1.length) == o2.length) {
1122                   for (key = 0; key < length; key++) {
1123                     if (!equals(o1[key], o2[key])) return false;
1124                   }
1125                   return true;
1126                 }
1127               } else if (isDate(o1)) {
1128                 if (!isDate(o2)) return false;
1129                 return equals(o1.getTime(), o2.getTime());
1130               } else if (isRegExp(o1)) {
1131                 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1132               } else {
1133                 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1134                   isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1135                 keySet = createMap();
1136                 for (key in o1) {
1137                   if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1138                   if (!equals(o1[key], o2[key])) return false;
1139                   keySet[key] = true;
1140                 }
1141                 for (key in o2) {
1142                   if (!(key in keySet) &&
1143                       key.charAt(0) !== '$' &&
1144                       isDefined(o2[key]) &&
1145                       !isFunction(o2[key])) return false;
1146                 }
1147                 return true;
1148               }
1149             }
1150           }
1151           return false;
1152         }
1153
1154         var csp = function() {
1155           if (!isDefined(csp.rules)) {
1156
1157
1158             var ngCspElement = (document.querySelector('[ng-csp]') ||
1159                             document.querySelector('[data-ng-csp]'));
1160
1161             if (ngCspElement) {
1162               var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1163                             ngCspElement.getAttribute('data-ng-csp');
1164               csp.rules = {
1165                 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1166                 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1167               };
1168             } else {
1169               csp.rules = {
1170                 noUnsafeEval: noUnsafeEval(),
1171                 noInlineStyle: false
1172               };
1173             }
1174           }
1175
1176           return csp.rules;
1177
1178           function noUnsafeEval() {
1179             try {
1180               /* jshint -W031, -W054 */
1181               new Function('');
1182               /* jshint +W031, +W054 */
1183               return false;
1184             } catch (e) {
1185               return true;
1186             }
1187           }
1188         };
1189
1190         /**
1191          * @ngdoc directive
1192          * @module ng
1193          * @name ngJq
1194          *
1195          * @element ANY
1196          * @param {string=} ngJq the name of the library available under `window`
1197          * to be used for angular.element
1198          * @description
1199          * Use this directive to force the angular.element library.  This should be
1200          * used to force either jqLite by leaving ng-jq blank or setting the name of
1201          * the jquery variable under window (eg. jQuery).
1202          *
1203          * Since angular looks for this directive when it is loaded (doesn't wait for the
1204          * DOMContentLoaded event), it must be placed on an element that comes before the script
1205          * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1206          * others ignored.
1207          *
1208          * @example
1209          * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1210          ```html
1211          <!doctype html>
1212          <html ng-app ng-jq>
1213          ...
1214          ...
1215          </html>
1216          ```
1217          * @example
1218          * This example shows how to use a jQuery based library of a different name.
1219          * The library name must be available at the top most 'window'.
1220          ```html
1221          <!doctype html>
1222          <html ng-app ng-jq="jQueryLib">
1223          ...
1224          ...
1225          </html>
1226          ```
1227          */
1228         var jq = function() {
1229           if (isDefined(jq.name_)) return jq.name_;
1230           var el;
1231           var i, ii = ngAttrPrefixes.length, prefix, name;
1232           for (i = 0; i < ii; ++i) {
1233             prefix = ngAttrPrefixes[i];
1234             if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1235               name = el.getAttribute(prefix + 'jq');
1236               break;
1237             }
1238           }
1239
1240           return (jq.name_ = name);
1241         };
1242
1243         function concat(array1, array2, index) {
1244           return array1.concat(slice.call(array2, index));
1245         }
1246
1247         function sliceArgs(args, startIndex) {
1248           return slice.call(args, startIndex || 0);
1249         }
1250
1251
1252         /* jshint -W101 */
1253         /**
1254          * @ngdoc function
1255          * @name angular.bind
1256          * @module ng
1257          * @kind function
1258          *
1259          * @description
1260          * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1261          * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1262          * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1263          * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1264          *
1265          * @param {Object} self Context which `fn` should be evaluated in.
1266          * @param {function()} fn Function to be bound.
1267          * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1268          * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1269          */
1270         /* jshint +W101 */
1271         function bind(self, fn) {
1272           var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1273           if (isFunction(fn) && !(fn instanceof RegExp)) {
1274             return curryArgs.length
1275               ? function() {
1276                   return arguments.length
1277                     ? fn.apply(self, concat(curryArgs, arguments, 0))
1278                     : fn.apply(self, curryArgs);
1279                 }
1280               : function() {
1281                   return arguments.length
1282                     ? fn.apply(self, arguments)
1283                     : fn.call(self);
1284                 };
1285           } else {
1286             // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1287             return fn;
1288           }
1289         }
1290
1291
1292         function toJsonReplacer(key, value) {
1293           var val = value;
1294
1295           if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1296             val = undefined;
1297           } else if (isWindow(value)) {
1298             val = '$WINDOW';
1299           } else if (value &&  document === value) {
1300             val = '$DOCUMENT';
1301           } else if (isScope(value)) {
1302             val = '$SCOPE';
1303           }
1304
1305           return val;
1306         }
1307
1308
1309         /**
1310          * @ngdoc function
1311          * @name angular.toJson
1312          * @module ng
1313          * @kind function
1314          *
1315          * @description
1316          * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1317          * stripped since angular uses this notation internally.
1318          *
1319          * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1320          * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1321          *    If set to an integer, the JSON output will contain that many spaces per indentation.
1322          * @returns {string|undefined} JSON-ified string representing `obj`.
1323          */
1324         function toJson(obj, pretty) {
1325           if (typeof obj === 'undefined') return undefined;
1326           if (!isNumber(pretty)) {
1327             pretty = pretty ? 2 : null;
1328           }
1329           return JSON.stringify(obj, toJsonReplacer, pretty);
1330         }
1331
1332
1333         /**
1334          * @ngdoc function
1335          * @name angular.fromJson
1336          * @module ng
1337          * @kind function
1338          *
1339          * @description
1340          * Deserializes a JSON string.
1341          *
1342          * @param {string} json JSON string to deserialize.
1343          * @returns {Object|Array|string|number} Deserialized JSON string.
1344          */
1345         function fromJson(json) {
1346           return isString(json)
1347               ? JSON.parse(json)
1348               : json;
1349         }
1350
1351
1352         function timezoneToOffset(timezone, fallback) {
1353           var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1354           return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1355         }
1356
1357
1358         function addDateMinutes(date, minutes) {
1359           date = new Date(date.getTime());
1360           date.setMinutes(date.getMinutes() + minutes);
1361           return date;
1362         }
1363
1364
1365         function convertTimezoneToLocal(date, timezone, reverse) {
1366           reverse = reverse ? -1 : 1;
1367           var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1368           return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1369         }
1370
1371
1372         /**
1373          * @returns {string} Returns the string representation of the element.
1374          */
1375         function startingTag(element) {
1376           element = jqLite(element).clone();
1377           try {
1378             // turns out IE does not let you set .html() on elements which
1379             // are not allowed to have children. So we just ignore it.
1380             element.empty();
1381           } catch (e) {}
1382           var elemHtml = jqLite('<div>').append(element).html();
1383           try {
1384             return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1385                 elemHtml.
1386                   match(/^(<[^>]+>)/)[1].
1387                   replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1388           } catch (e) {
1389             return lowercase(elemHtml);
1390           }
1391
1392         }
1393
1394
1395         /////////////////////////////////////////////////
1396
1397         /**
1398          * Tries to decode the URI component without throwing an exception.
1399          *
1400          * @private
1401          * @param str value potential URI component to check.
1402          * @returns {boolean} True if `value` can be decoded
1403          * with the decodeURIComponent function.
1404          */
1405         function tryDecodeURIComponent(value) {
1406           try {
1407             return decodeURIComponent(value);
1408           } catch (e) {
1409             // Ignore any invalid uri component
1410           }
1411         }
1412
1413
1414         /**
1415          * Parses an escaped url query string into key-value pairs.
1416          * @returns {Object.<string,boolean|Array>}
1417          */
1418         function parseKeyValue(/**string*/keyValue) {
1419           var obj = {};
1420           forEach((keyValue || "").split('&'), function(keyValue) {
1421             var splitPoint, key, val;
1422             if (keyValue) {
1423               key = keyValue = keyValue.replace(/\+/g,'%20');
1424               splitPoint = keyValue.indexOf('=');
1425               if (splitPoint !== -1) {
1426                 key = keyValue.substring(0, splitPoint);
1427                 val = keyValue.substring(splitPoint + 1);
1428               }
1429               key = tryDecodeURIComponent(key);
1430               if (isDefined(key)) {
1431                 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1432                 if (!hasOwnProperty.call(obj, key)) {
1433                   obj[key] = val;
1434                 } else if (isArray(obj[key])) {
1435                   obj[key].push(val);
1436                 } else {
1437                   obj[key] = [obj[key],val];
1438                 }
1439               }
1440             }
1441           });
1442           return obj;
1443         }
1444
1445         function toKeyValue(obj) {
1446           var parts = [];
1447           forEach(obj, function(value, key) {
1448             if (isArray(value)) {
1449               forEach(value, function(arrayValue) {
1450                 parts.push(encodeUriQuery(key, true) +
1451                            (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1452               });
1453             } else {
1454             parts.push(encodeUriQuery(key, true) +
1455                        (value === true ? '' : '=' + encodeUriQuery(value, true)));
1456             }
1457           });
1458           return parts.length ? parts.join('&') : '';
1459         }
1460
1461
1462         /**
1463          * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1464          * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1465          * segments:
1466          *    segment       = *pchar
1467          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1468          *    pct-encoded   = "%" HEXDIG HEXDIG
1469          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1470          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1471          *                     / "*" / "+" / "," / ";" / "="
1472          */
1473         function encodeUriSegment(val) {
1474           return encodeUriQuery(val, true).
1475                      replace(/%26/gi, '&').
1476                      replace(/%3D/gi, '=').
1477                      replace(/%2B/gi, '+');
1478         }
1479
1480
1481         /**
1482          * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1483          * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1484          * encoded per http://tools.ietf.org/html/rfc3986:
1485          *    query       = *( pchar / "/" / "?" )
1486          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1487          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1488          *    pct-encoded   = "%" HEXDIG HEXDIG
1489          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1490          *                     / "*" / "+" / "," / ";" / "="
1491          */
1492         function encodeUriQuery(val, pctEncodeSpaces) {
1493           return encodeURIComponent(val).
1494                      replace(/%40/gi, '@').
1495                      replace(/%3A/gi, ':').
1496                      replace(/%24/g, '$').
1497                      replace(/%2C/gi, ',').
1498                      replace(/%3B/gi, ';').
1499                      replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1500         }
1501
1502         var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1503
1504         function getNgAttribute(element, ngAttr) {
1505           var attr, i, ii = ngAttrPrefixes.length;
1506           for (i = 0; i < ii; ++i) {
1507             attr = ngAttrPrefixes[i] + ngAttr;
1508             if (isString(attr = element.getAttribute(attr))) {
1509               return attr;
1510             }
1511           }
1512           return null;
1513         }
1514
1515         /**
1516          * @ngdoc directive
1517          * @name ngApp
1518          * @module ng
1519          *
1520          * @element ANY
1521          * @param {angular.Module} ngApp an optional application
1522          *   {@link angular.module module} name to load.
1523          * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1524          *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1525          *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1526          *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1527          *   tracking down the root of these bugs.
1528          *
1529          * @description
1530          *
1531          * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1532          * designates the **root element** of the application and is typically placed near the root element
1533          * of the page - e.g. on the `<body>` or `<html>` tags.
1534          *
1535          * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1536          * found in the document will be used to define the root element to auto-bootstrap as an
1537          * application. To run multiple applications in an HTML document you must manually bootstrap them using
1538          * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1539          *
1540          * You can specify an **AngularJS module** to be used as the root module for the application.  This
1541          * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1542          * should contain the application code needed or have dependencies on other modules that will
1543          * contain the code. See {@link angular.module} for more information.
1544          *
1545          * In the example below if the `ngApp` directive were not placed on the `html` element then the
1546          * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1547          * would not be resolved to `3`.
1548          *
1549          * `ngApp` is the easiest, and most common way to bootstrap an application.
1550          *
1551          <example module="ngAppDemo">
1552            <file name="index.html">
1553            <div ng-controller="ngAppDemoController">
1554              I can add: {{a}} + {{b}} =  {{ a+b }}
1555            </div>
1556            </file>
1557            <file name="script.js">
1558            angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1559              $scope.a = 1;
1560              $scope.b = 2;
1561            });
1562            </file>
1563          </example>
1564          *
1565          * Using `ngStrictDi`, you would see something like this:
1566          *
1567          <example ng-app-included="true">
1568            <file name="index.html">
1569            <div ng-app="ngAppStrictDemo" ng-strict-di>
1570                <div ng-controller="GoodController1">
1571                    I can add: {{a}} + {{b}} =  {{ a+b }}
1572
1573                    <p>This renders because the controller does not fail to
1574                       instantiate, by using explicit annotation style (see
1575                       script.js for details)
1576                    </p>
1577                </div>
1578
1579                <div ng-controller="GoodController2">
1580                    Name: <input ng-model="name"><br />
1581                    Hello, {{name}}!
1582
1583                    <p>This renders because the controller does not fail to
1584                       instantiate, by using explicit annotation style
1585                       (see script.js for details)
1586                    </p>
1587                </div>
1588
1589                <div ng-controller="BadController">
1590                    I can add: {{a}} + {{b}} =  {{ a+b }}
1591
1592                    <p>The controller could not be instantiated, due to relying
1593                       on automatic function annotations (which are disabled in
1594                       strict mode). As such, the content of this section is not
1595                       interpolated, and there should be an error in your web console.
1596                    </p>
1597                </div>
1598            </div>
1599            </file>
1600            <file name="script.js">
1601            angular.module('ngAppStrictDemo', [])
1602              // BadController will fail to instantiate, due to relying on automatic function annotation,
1603              // rather than an explicit annotation
1604              .controller('BadController', function($scope) {
1605                $scope.a = 1;
1606                $scope.b = 2;
1607              })
1608              // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1609              // due to using explicit annotations using the array style and $inject property, respectively.
1610              .controller('GoodController1', ['$scope', function($scope) {
1611                $scope.a = 1;
1612                $scope.b = 2;
1613              }])
1614              .controller('GoodController2', GoodController2);
1615              function GoodController2($scope) {
1616                $scope.name = "World";
1617              }
1618              GoodController2.$inject = ['$scope'];
1619            </file>
1620            <file name="style.css">
1621            div[ng-controller] {
1622                margin-bottom: 1em;
1623                -webkit-border-radius: 4px;
1624                border-radius: 4px;
1625                border: 1px solid;
1626                padding: .5em;
1627            }
1628            div[ng-controller^=Good] {
1629                border-color: #d6e9c6;
1630                background-color: #dff0d8;
1631                color: #3c763d;
1632            }
1633            div[ng-controller^=Bad] {
1634                border-color: #ebccd1;
1635                background-color: #f2dede;
1636                color: #a94442;
1637                margin-bottom: 0;
1638            }
1639            </file>
1640          </example>
1641          */
1642         function angularInit(element, bootstrap) {
1643           var appElement,
1644               module,
1645               config = {};
1646
1647           // The element `element` has priority over any other element
1648           forEach(ngAttrPrefixes, function(prefix) {
1649             var name = prefix + 'app';
1650
1651             if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1652               appElement = element;
1653               module = element.getAttribute(name);
1654             }
1655           });
1656           forEach(ngAttrPrefixes, function(prefix) {
1657             var name = prefix + 'app';
1658             var candidate;
1659
1660             if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1661               appElement = candidate;
1662               module = candidate.getAttribute(name);
1663             }
1664           });
1665           if (appElement) {
1666             config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1667             bootstrap(appElement, module ? [module] : [], config);
1668           }
1669         }
1670
1671         /**
1672          * @ngdoc function
1673          * @name angular.bootstrap
1674          * @module ng
1675          * @description
1676          * Use this function to manually start up angular application.
1677          *
1678          * See: {@link guide/bootstrap Bootstrap}
1679          *
1680          * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1681          * They must use {@link ng.directive:ngApp ngApp}.
1682          *
1683          * Angular will detect if it has been loaded into the browser more than once and only allow the
1684          * first loaded script to be bootstrapped and will report a warning to the browser console for
1685          * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1686          * multiple instances of Angular try to work on the DOM.
1687          *
1688          * ```html
1689          * <!doctype html>
1690          * <html>
1691          * <body>
1692          * <div ng-controller="WelcomeController">
1693          *   {{greeting}}
1694          * </div>
1695          *
1696          * <script src="angular.js"></script>
1697          * <script>
1698          *   var app = angular.module('demo', [])
1699          *   .controller('WelcomeController', function($scope) {
1700          *       $scope.greeting = 'Welcome!';
1701          *   });
1702          *   angular.bootstrap(document, ['demo']);
1703          * </script>
1704          * </body>
1705          * </html>
1706          * ```
1707          *
1708          * @param {DOMElement} element DOM element which is the root of angular application.
1709          * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1710          *     Each item in the array should be the name of a predefined module or a (DI annotated)
1711          *     function that will be invoked by the injector as a `config` block.
1712          *     See: {@link angular.module modules}
1713          * @param {Object=} config an object for defining configuration options for the application. The
1714          *     following keys are supported:
1715          *
1716          * * `strictDi` - disable automatic function annotation for the application. This is meant to
1717          *   assist in finding bugs which break minified code. Defaults to `false`.
1718          *
1719          * @returns {auto.$injector} Returns the newly created injector for this app.
1720          */
1721         function bootstrap(element, modules, config) {
1722           if (!isObject(config)) config = {};
1723           var defaultConfig = {
1724             strictDi: false
1725           };
1726           config = extend(defaultConfig, config);
1727           var doBootstrap = function() {
1728             element = jqLite(element);
1729
1730             if (element.injector()) {
1731               var tag = (element[0] === document) ? 'document' : startingTag(element);
1732               //Encode angle brackets to prevent input from being sanitized to empty string #8683
1733               throw ngMinErr(
1734                   'btstrpd',
1735                   "App Already Bootstrapped with this Element '{0}'",
1736                   tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1737             }
1738
1739             modules = modules || [];
1740             modules.unshift(['$provide', function($provide) {
1741               $provide.value('$rootElement', element);
1742             }]);
1743
1744             if (config.debugInfoEnabled) {
1745               // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1746               modules.push(['$compileProvider', function($compileProvider) {
1747                 $compileProvider.debugInfoEnabled(true);
1748               }]);
1749             }
1750
1751             modules.unshift('ng');
1752             var injector = createInjector(modules, config.strictDi);
1753             injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1754                function bootstrapApply(scope, element, compile, injector) {
1755                 scope.$apply(function() {
1756                   element.data('$injector', injector);
1757                   compile(element)(scope);
1758                 });
1759               }]
1760             );
1761             return injector;
1762           };
1763
1764           var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1765           var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1766
1767           if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1768             config.debugInfoEnabled = true;
1769             window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1770           }
1771
1772           if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1773             return doBootstrap();
1774           }
1775
1776           window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1777           angular.resumeBootstrap = function(extraModules) {
1778             forEach(extraModules, function(module) {
1779               modules.push(module);
1780             });
1781             return doBootstrap();
1782           };
1783
1784           if (isFunction(angular.resumeDeferredBootstrap)) {
1785             angular.resumeDeferredBootstrap();
1786           }
1787         }
1788
1789         /**
1790          * @ngdoc function
1791          * @name angular.reloadWithDebugInfo
1792          * @module ng
1793          * @description
1794          * Use this function to reload the current application with debug information turned on.
1795          * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1796          *
1797          * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1798          */
1799         function reloadWithDebugInfo() {
1800           window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1801           window.location.reload();
1802         }
1803
1804         /**
1805          * @name angular.getTestability
1806          * @module ng
1807          * @description
1808          * Get the testability service for the instance of Angular on the given
1809          * element.
1810          * @param {DOMElement} element DOM element which is the root of angular application.
1811          */
1812         function getTestability(rootElement) {
1813           var injector = angular.element(rootElement).injector();
1814           if (!injector) {
1815             throw ngMinErr('test',
1816               'no injector found for element argument to getTestability');
1817           }
1818           return injector.get('$$testability');
1819         }
1820
1821         var SNAKE_CASE_REGEXP = /[A-Z]/g;
1822         function snake_case(name, separator) {
1823           separator = separator || '_';
1824           return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1825             return (pos ? separator : '') + letter.toLowerCase();
1826           });
1827         }
1828
1829         var bindJQueryFired = false;
1830         var skipDestroyOnNextJQueryCleanData;
1831         function bindJQuery() {
1832           var originalCleanData;
1833
1834           if (bindJQueryFired) {
1835             return;
1836           }
1837
1838           // bind to jQuery if present;
1839           var jqName = jq();
1840           jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1841                    !jqName             ? undefined     :   // use jqLite
1842                                          window[jqName];   // use jQuery specified by `ngJq`
1843
1844           // Use jQuery if it exists with proper functionality, otherwise default to us.
1845           // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1846           // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1847           // versions. It will not work for sure with jQuery <1.7, though.
1848           if (jQuery && jQuery.fn.on) {
1849             jqLite = jQuery;
1850             extend(jQuery.fn, {
1851               scope: JQLitePrototype.scope,
1852               isolateScope: JQLitePrototype.isolateScope,
1853               controller: JQLitePrototype.controller,
1854               injector: JQLitePrototype.injector,
1855               inheritedData: JQLitePrototype.inheritedData
1856             });
1857
1858             // All nodes removed from the DOM via various jQuery APIs like .remove()
1859             // are passed through jQuery.cleanData. Monkey-patch this method to fire
1860             // the $destroy event on all removed nodes.
1861             originalCleanData = jQuery.cleanData;
1862             jQuery.cleanData = function(elems) {
1863               var events;
1864               if (!skipDestroyOnNextJQueryCleanData) {
1865                 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1866                   events = jQuery._data(elem, "events");
1867                   if (events && events.$destroy) {
1868                     jQuery(elem).triggerHandler('$destroy');
1869                   }
1870                 }
1871               } else {
1872                 skipDestroyOnNextJQueryCleanData = false;
1873               }
1874               originalCleanData(elems);
1875             };
1876           } else {
1877             jqLite = JQLite;
1878           }
1879
1880           angular.element = jqLite;
1881
1882           // Prevent double-proxying.
1883           bindJQueryFired = true;
1884         }
1885
1886         /**
1887          * throw error if the argument is falsy.
1888          */
1889         function assertArg(arg, name, reason) {
1890           if (!arg) {
1891             throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1892           }
1893           return arg;
1894         }
1895
1896         function assertArgFn(arg, name, acceptArrayAnnotation) {
1897           if (acceptArrayAnnotation && isArray(arg)) {
1898               arg = arg[arg.length - 1];
1899           }
1900
1901           assertArg(isFunction(arg), name, 'not a function, got ' +
1902               (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1903           return arg;
1904         }
1905
1906         /**
1907          * throw error if the name given is hasOwnProperty
1908          * @param  {String} name    the name to test
1909          * @param  {String} context the context in which the name is used, such as module or directive
1910          */
1911         function assertNotHasOwnProperty(name, context) {
1912           if (name === 'hasOwnProperty') {
1913             throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1914           }
1915         }
1916
1917         /**
1918          * Return the value accessible from the object by path. Any undefined traversals are ignored
1919          * @param {Object} obj starting object
1920          * @param {String} path path to traverse
1921          * @param {boolean} [bindFnToScope=true]
1922          * @returns {Object} value as accessible by path
1923          */
1924         //TODO(misko): this function needs to be removed
1925         function getter(obj, path, bindFnToScope) {
1926           if (!path) return obj;
1927           var keys = path.split('.');
1928           var key;
1929           var lastInstance = obj;
1930           var len = keys.length;
1931
1932           for (var i = 0; i < len; i++) {
1933             key = keys[i];
1934             if (obj) {
1935               obj = (lastInstance = obj)[key];
1936             }
1937           }
1938           if (!bindFnToScope && isFunction(obj)) {
1939             return bind(lastInstance, obj);
1940           }
1941           return obj;
1942         }
1943
1944         /**
1945          * Return the DOM siblings between the first and last node in the given array.
1946          * @param {Array} array like object
1947          * @returns {Array} the inputted object or a jqLite collection containing the nodes
1948          */
1949         function getBlockNodes(nodes) {
1950           // TODO(perf): update `nodes` instead of creating a new object?
1951           var node = nodes[0];
1952           var endNode = nodes[nodes.length - 1];
1953           var blockNodes;
1954
1955           for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1956             if (blockNodes || nodes[i] !== node) {
1957               if (!blockNodes) {
1958                 blockNodes = jqLite(slice.call(nodes, 0, i));
1959               }
1960               blockNodes.push(node);
1961             }
1962           }
1963
1964           return blockNodes || nodes;
1965         }
1966
1967
1968         /**
1969          * Creates a new object without a prototype. This object is useful for lookup without having to
1970          * guard against prototypically inherited properties via hasOwnProperty.
1971          *
1972          * Related micro-benchmarks:
1973          * - http://jsperf.com/object-create2
1974          * - http://jsperf.com/proto-map-lookup/2
1975          * - http://jsperf.com/for-in-vs-object-keys2
1976          *
1977          * @returns {Object}
1978          */
1979         function createMap() {
1980           return Object.create(null);
1981         }
1982
1983         var NODE_TYPE_ELEMENT = 1;
1984         var NODE_TYPE_ATTRIBUTE = 2;
1985         var NODE_TYPE_TEXT = 3;
1986         var NODE_TYPE_COMMENT = 8;
1987         var NODE_TYPE_DOCUMENT = 9;
1988         var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1989
1990         /**
1991          * @ngdoc type
1992          * @name angular.Module
1993          * @module ng
1994          * @description
1995          *
1996          * Interface for configuring angular {@link angular.module modules}.
1997          */
1998
1999         function setupModuleLoader(window) {
2000
2001           var $injectorMinErr = minErr('$injector');
2002           var ngMinErr = minErr('ng');
2003
2004           function ensure(obj, name, factory) {
2005             return obj[name] || (obj[name] = factory());
2006           }
2007
2008           var angular = ensure(window, 'angular', Object);
2009
2010           // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2011           angular.$$minErr = angular.$$minErr || minErr;
2012
2013           return ensure(angular, 'module', function() {
2014             /** @type {Object.<string, angular.Module>} */
2015             var modules = {};
2016
2017             /**
2018              * @ngdoc function
2019              * @name angular.module
2020              * @module ng
2021              * @description
2022              *
2023              * The `angular.module` is a global place for creating, registering and retrieving Angular
2024              * modules.
2025              * All modules (angular core or 3rd party) that should be available to an application must be
2026              * registered using this mechanism.
2027              *
2028              * Passing one argument retrieves an existing {@link angular.Module},
2029              * whereas passing more than one argument creates a new {@link angular.Module}
2030              *
2031              *
2032              * # Module
2033              *
2034              * A module is a collection of services, directives, controllers, filters, and configuration information.
2035              * `angular.module` is used to configure the {@link auto.$injector $injector}.
2036              *
2037              * ```js
2038              * // Create a new module
2039              * var myModule = angular.module('myModule', []);
2040              *
2041              * // register a new service
2042              * myModule.value('appName', 'MyCoolApp');
2043              *
2044              * // configure existing services inside initialization blocks.
2045              * myModule.config(['$locationProvider', function($locationProvider) {
2046              *   // Configure existing providers
2047              *   $locationProvider.hashPrefix('!');
2048              * }]);
2049              * ```
2050              *
2051              * Then you can create an injector and load your modules like this:
2052              *
2053              * ```js
2054              * var injector = angular.injector(['ng', 'myModule'])
2055              * ```
2056              *
2057              * However it's more likely that you'll just use
2058              * {@link ng.directive:ngApp ngApp} or
2059              * {@link angular.bootstrap} to simplify this process for you.
2060              *
2061              * @param {!string} name The name of the module to create or retrieve.
2062              * @param {!Array.<string>=} requires If specified then new module is being created. If
2063              *        unspecified then the module is being retrieved for further configuration.
2064              * @param {Function=} configFn Optional configuration function for the module. Same as
2065              *        {@link angular.Module#config Module#config()}.
2066              * @returns {module} new module with the {@link angular.Module} api.
2067              */
2068             return function module(name, requires, configFn) {
2069               var assertNotHasOwnProperty = function(name, context) {
2070                 if (name === 'hasOwnProperty') {
2071                   throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2072                 }
2073               };
2074
2075               assertNotHasOwnProperty(name, 'module');
2076               if (requires && modules.hasOwnProperty(name)) {
2077                 modules[name] = null;
2078               }
2079               return ensure(modules, name, function() {
2080                 if (!requires) {
2081                   throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2082                      "the module name or forgot to load it. If registering a module ensure that you " +
2083                      "specify the dependencies as the second argument.", name);
2084                 }
2085
2086                 /** @type {!Array.<Array.<*>>} */
2087                 var invokeQueue = [];
2088
2089                 /** @type {!Array.<Function>} */
2090                 var configBlocks = [];
2091
2092                 /** @type {!Array.<Function>} */
2093                 var runBlocks = [];
2094
2095                 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2096
2097                 /** @type {angular.Module} */
2098                 var moduleInstance = {
2099                   // Private state
2100                   _invokeQueue: invokeQueue,
2101                   _configBlocks: configBlocks,
2102                   _runBlocks: runBlocks,
2103
2104                   /**
2105                    * @ngdoc property
2106                    * @name angular.Module#requires
2107                    * @module ng
2108                    *
2109                    * @description
2110                    * Holds the list of modules which the injector will load before the current module is
2111                    * loaded.
2112                    */
2113                   requires: requires,
2114
2115                   /**
2116                    * @ngdoc property
2117                    * @name angular.Module#name
2118                    * @module ng
2119                    *
2120                    * @description
2121                    * Name of the module.
2122                    */
2123                   name: name,
2124
2125
2126                   /**
2127                    * @ngdoc method
2128                    * @name angular.Module#provider
2129                    * @module ng
2130                    * @param {string} name service name
2131                    * @param {Function} providerType Construction function for creating new instance of the
2132                    *                                service.
2133                    * @description
2134                    * See {@link auto.$provide#provider $provide.provider()}.
2135                    */
2136                   provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2137
2138                   /**
2139                    * @ngdoc method
2140                    * @name angular.Module#factory
2141                    * @module ng
2142                    * @param {string} name service name
2143                    * @param {Function} providerFunction Function for creating new instance of the service.
2144                    * @description
2145                    * See {@link auto.$provide#factory $provide.factory()}.
2146                    */
2147                   factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2148
2149                   /**
2150                    * @ngdoc method
2151                    * @name angular.Module#service
2152                    * @module ng
2153                    * @param {string} name service name
2154                    * @param {Function} constructor A constructor function that will be instantiated.
2155                    * @description
2156                    * See {@link auto.$provide#service $provide.service()}.
2157                    */
2158                   service: invokeLaterAndSetModuleName('$provide', 'service'),
2159
2160                   /**
2161                    * @ngdoc method
2162                    * @name angular.Module#value
2163                    * @module ng
2164                    * @param {string} name service name
2165                    * @param {*} object Service instance object.
2166                    * @description
2167                    * See {@link auto.$provide#value $provide.value()}.
2168                    */
2169                   value: invokeLater('$provide', 'value'),
2170
2171                   /**
2172                    * @ngdoc method
2173                    * @name angular.Module#constant
2174                    * @module ng
2175                    * @param {string} name constant name
2176                    * @param {*} object Constant value.
2177                    * @description
2178                    * Because the constants are fixed, they get applied before other provide methods.
2179                    * See {@link auto.$provide#constant $provide.constant()}.
2180                    */
2181                   constant: invokeLater('$provide', 'constant', 'unshift'),
2182
2183                    /**
2184                    * @ngdoc method
2185                    * @name angular.Module#decorator
2186                    * @module ng
2187                    * @param {string} The name of the service to decorate.
2188                    * @param {Function} This function will be invoked when the service needs to be
2189                    *                                    instantiated and should return the decorated service instance.
2190                    * @description
2191                    * See {@link auto.$provide#decorator $provide.decorator()}.
2192                    */
2193                   decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2194
2195                   /**
2196                    * @ngdoc method
2197                    * @name angular.Module#animation
2198                    * @module ng
2199                    * @param {string} name animation name
2200                    * @param {Function} animationFactory Factory function for creating new instance of an
2201                    *                                    animation.
2202                    * @description
2203                    *
2204                    * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2205                    *
2206                    *
2207                    * Defines an animation hook that can be later used with
2208                    * {@link $animate $animate} service and directives that use this service.
2209                    *
2210                    * ```js
2211                    * module.animation('.animation-name', function($inject1, $inject2) {
2212                    *   return {
2213                    *     eventName : function(element, done) {
2214                    *       //code to run the animation
2215                    *       //once complete, then run done()
2216                    *       return function cancellationFunction(element) {
2217                    *         //code to cancel the animation
2218                    *       }
2219                    *     }
2220                    *   }
2221                    * })
2222                    * ```
2223                    *
2224                    * See {@link ng.$animateProvider#register $animateProvider.register()} and
2225                    * {@link ngAnimate ngAnimate module} for more information.
2226                    */
2227                   animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2228
2229                   /**
2230                    * @ngdoc method
2231                    * @name angular.Module#filter
2232                    * @module ng
2233                    * @param {string} name Filter name - this must be a valid angular expression identifier
2234                    * @param {Function} filterFactory Factory function for creating new instance of filter.
2235                    * @description
2236                    * See {@link ng.$filterProvider#register $filterProvider.register()}.
2237                    *
2238                    * <div class="alert alert-warning">
2239                    * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2240                    * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2241                    * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2242                    * (`myapp_subsection_filterx`).
2243                    * </div>
2244                    */
2245                   filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2246
2247                   /**
2248                    * @ngdoc method
2249                    * @name angular.Module#controller
2250                    * @module ng
2251                    * @param {string|Object} name Controller name, or an object map of controllers where the
2252                    *    keys are the names and the values are the constructors.
2253                    * @param {Function} constructor Controller constructor function.
2254                    * @description
2255                    * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2256                    */
2257                   controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2258
2259                   /**
2260                    * @ngdoc method
2261                    * @name angular.Module#directive
2262                    * @module ng
2263                    * @param {string|Object} name Directive name, or an object map of directives where the
2264                    *    keys are the names and the values are the factories.
2265                    * @param {Function} directiveFactory Factory function for creating new instance of
2266                    * directives.
2267                    * @description
2268                    * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2269                    */
2270                   directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2271
2272                   /**
2273                    * @ngdoc method
2274                    * @name angular.Module#config
2275                    * @module ng
2276                    * @param {Function} configFn Execute this function on module load. Useful for service
2277                    *    configuration.
2278                    * @description
2279                    * Use this method to register work which needs to be performed on module loading.
2280                    * For more about how to configure services, see
2281                    * {@link providers#provider-recipe Provider Recipe}.
2282                    */
2283                   config: config,
2284
2285                   /**
2286                    * @ngdoc method
2287                    * @name angular.Module#run
2288                    * @module ng
2289                    * @param {Function} initializationFn Execute this function after injector creation.
2290                    *    Useful for application initialization.
2291                    * @description
2292                    * Use this method to register work which should be performed when the injector is done
2293                    * loading all modules.
2294                    */
2295                   run: function(block) {
2296                     runBlocks.push(block);
2297                     return this;
2298                   }
2299                 };
2300
2301                 if (configFn) {
2302                   config(configFn);
2303                 }
2304
2305                 return moduleInstance;
2306
2307                 /**
2308                  * @param {string} provider
2309                  * @param {string} method
2310                  * @param {String=} insertMethod
2311                  * @returns {angular.Module}
2312                  */
2313                 function invokeLater(provider, method, insertMethod, queue) {
2314                   if (!queue) queue = invokeQueue;
2315                   return function() {
2316                     queue[insertMethod || 'push']([provider, method, arguments]);
2317                     return moduleInstance;
2318                   };
2319                 }
2320
2321                 /**
2322                  * @param {string} provider
2323                  * @param {string} method
2324                  * @returns {angular.Module}
2325                  */
2326                 function invokeLaterAndSetModuleName(provider, method) {
2327                   return function(recipeName, factoryFunction) {
2328                     if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2329                     invokeQueue.push([provider, method, arguments]);
2330                     return moduleInstance;
2331                   };
2332                 }
2333               });
2334             };
2335           });
2336
2337         }
2338
2339         /* global: toDebugString: true */
2340
2341         function serializeObject(obj) {
2342           var seen = [];
2343
2344           return JSON.stringify(obj, function(key, val) {
2345             val = toJsonReplacer(key, val);
2346             if (isObject(val)) {
2347
2348               if (seen.indexOf(val) >= 0) return '...';
2349
2350               seen.push(val);
2351             }
2352             return val;
2353           });
2354         }
2355
2356         function toDebugString(obj) {
2357           if (typeof obj === 'function') {
2358             return obj.toString().replace(/ \{[\s\S]*$/, '');
2359           } else if (isUndefined(obj)) {
2360             return 'undefined';
2361           } else if (typeof obj !== 'string') {
2362             return serializeObject(obj);
2363           }
2364           return obj;
2365         }
2366
2367         /* global angularModule: true,
2368           version: true,
2369
2370           $CompileProvider,
2371
2372           htmlAnchorDirective,
2373           inputDirective,
2374           inputDirective,
2375           formDirective,
2376           scriptDirective,
2377           selectDirective,
2378           styleDirective,
2379           optionDirective,
2380           ngBindDirective,
2381           ngBindHtmlDirective,
2382           ngBindTemplateDirective,
2383           ngClassDirective,
2384           ngClassEvenDirective,
2385           ngClassOddDirective,
2386           ngCloakDirective,
2387           ngControllerDirective,
2388           ngFormDirective,
2389           ngHideDirective,
2390           ngIfDirective,
2391           ngIncludeDirective,
2392           ngIncludeFillContentDirective,
2393           ngInitDirective,
2394           ngNonBindableDirective,
2395           ngPluralizeDirective,
2396           ngRepeatDirective,
2397           ngShowDirective,
2398           ngStyleDirective,
2399           ngSwitchDirective,
2400           ngSwitchWhenDirective,
2401           ngSwitchDefaultDirective,
2402           ngOptionsDirective,
2403           ngTranscludeDirective,
2404           ngModelDirective,
2405           ngListDirective,
2406           ngChangeDirective,
2407           patternDirective,
2408           patternDirective,
2409           requiredDirective,
2410           requiredDirective,
2411           minlengthDirective,
2412           minlengthDirective,
2413           maxlengthDirective,
2414           maxlengthDirective,
2415           ngValueDirective,
2416           ngModelOptionsDirective,
2417           ngAttributeAliasDirectives,
2418           ngEventDirectives,
2419
2420           $AnchorScrollProvider,
2421           $AnimateProvider,
2422           $CoreAnimateCssProvider,
2423           $$CoreAnimateQueueProvider,
2424           $$CoreAnimateRunnerProvider,
2425           $BrowserProvider,
2426           $CacheFactoryProvider,
2427           $ControllerProvider,
2428           $DocumentProvider,
2429           $ExceptionHandlerProvider,
2430           $FilterProvider,
2431           $$ForceReflowProvider,
2432           $InterpolateProvider,
2433           $IntervalProvider,
2434           $$HashMapProvider,
2435           $HttpProvider,
2436           $HttpParamSerializerProvider,
2437           $HttpParamSerializerJQLikeProvider,
2438           $HttpBackendProvider,
2439           $xhrFactoryProvider,
2440           $LocationProvider,
2441           $LogProvider,
2442           $ParseProvider,
2443           $RootScopeProvider,
2444           $QProvider,
2445           $$QProvider,
2446           $$SanitizeUriProvider,
2447           $SceProvider,
2448           $SceDelegateProvider,
2449           $SnifferProvider,
2450           $TemplateCacheProvider,
2451           $TemplateRequestProvider,
2452           $$TestabilityProvider,
2453           $TimeoutProvider,
2454           $$RAFProvider,
2455           $WindowProvider,
2456           $$jqLiteProvider,
2457           $$CookieReaderProvider
2458         */
2459
2460
2461         /**
2462          * @ngdoc object
2463          * @name angular.version
2464          * @module ng
2465          * @description
2466          * An object that contains information about the current AngularJS version.
2467          *
2468          * This object has the following properties:
2469          *
2470          * - `full` – `{string}` – Full version string, such as "0.9.18".
2471          * - `major` – `{number}` – Major version number, such as "0".
2472          * - `minor` – `{number}` – Minor version number, such as "9".
2473          * - `dot` – `{number}` – Dot version number, such as "18".
2474          * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2475          */
2476         var version = {
2477           full: '1.4.8',    // all of these placeholder strings will be replaced by grunt's
2478           major: 1,    // package task
2479           minor: 4,
2480           dot: 8,
2481           codeName: 'ice-manipulation'
2482         };
2483
2484
2485         function publishExternalAPI(angular) {
2486           extend(angular, {
2487             'bootstrap': bootstrap,
2488             'copy': copy,
2489             'extend': extend,
2490             'merge': merge,
2491             'equals': equals,
2492             'element': jqLite,
2493             'forEach': forEach,
2494             'injector': createInjector,
2495             'noop': noop,
2496             'bind': bind,
2497             'toJson': toJson,
2498             'fromJson': fromJson,
2499             'identity': identity,
2500             'isUndefined': isUndefined,
2501             'isDefined': isDefined,
2502             'isString': isString,
2503             'isFunction': isFunction,
2504             'isObject': isObject,
2505             'isNumber': isNumber,
2506             'isElement': isElement,
2507             'isArray': isArray,
2508             'version': version,
2509             'isDate': isDate,
2510             'lowercase': lowercase,
2511             'uppercase': uppercase,
2512             'callbacks': {counter: 0},
2513             'getTestability': getTestability,
2514             '$$minErr': minErr,
2515             '$$csp': csp,
2516             'reloadWithDebugInfo': reloadWithDebugInfo
2517           });
2518
2519           angularModule = setupModuleLoader(window);
2520
2521           angularModule('ng', ['ngLocale'], ['$provide',
2522             function ngModule($provide) {
2523               // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2524               $provide.provider({
2525                 $$sanitizeUri: $$SanitizeUriProvider
2526               });
2527               $provide.provider('$compile', $CompileProvider).
2528                 directive({
2529                     a: htmlAnchorDirective,
2530                     input: inputDirective,
2531                     textarea: inputDirective,
2532                     form: formDirective,
2533                     script: scriptDirective,
2534                     select: selectDirective,
2535                     style: styleDirective,
2536                     option: optionDirective,
2537                     ngBind: ngBindDirective,
2538                     ngBindHtml: ngBindHtmlDirective,
2539                     ngBindTemplate: ngBindTemplateDirective,
2540                     ngClass: ngClassDirective,
2541                     ngClassEven: ngClassEvenDirective,
2542                     ngClassOdd: ngClassOddDirective,
2543                     ngCloak: ngCloakDirective,
2544                     ngController: ngControllerDirective,
2545                     ngForm: ngFormDirective,
2546                     ngHide: ngHideDirective,
2547                     ngIf: ngIfDirective,
2548                     ngInclude: ngIncludeDirective,
2549                     ngInit: ngInitDirective,
2550                     ngNonBindable: ngNonBindableDirective,
2551                     ngPluralize: ngPluralizeDirective,
2552                     ngRepeat: ngRepeatDirective,
2553                     ngShow: ngShowDirective,
2554                     ngStyle: ngStyleDirective,
2555                     ngSwitch: ngSwitchDirective,
2556                     ngSwitchWhen: ngSwitchWhenDirective,
2557                     ngSwitchDefault: ngSwitchDefaultDirective,
2558                     ngOptions: ngOptionsDirective,
2559                     ngTransclude: ngTranscludeDirective,
2560                     ngModel: ngModelDirective,
2561                     ngList: ngListDirective,
2562                     ngChange: ngChangeDirective,
2563                     pattern: patternDirective,
2564                     ngPattern: patternDirective,
2565                     required: requiredDirective,
2566                     ngRequired: requiredDirective,
2567                     minlength: minlengthDirective,
2568                     ngMinlength: minlengthDirective,
2569                     maxlength: maxlengthDirective,
2570                     ngMaxlength: maxlengthDirective,
2571                     ngValue: ngValueDirective,
2572                     ngModelOptions: ngModelOptionsDirective
2573                 }).
2574                 directive({
2575                   ngInclude: ngIncludeFillContentDirective
2576                 }).
2577                 directive(ngAttributeAliasDirectives).
2578                 directive(ngEventDirectives);
2579               $provide.provider({
2580                 $anchorScroll: $AnchorScrollProvider,
2581                 $animate: $AnimateProvider,
2582                 $animateCss: $CoreAnimateCssProvider,
2583                 $$animateQueue: $$CoreAnimateQueueProvider,
2584                 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2585                 $browser: $BrowserProvider,
2586                 $cacheFactory: $CacheFactoryProvider,
2587                 $controller: $ControllerProvider,
2588                 $document: $DocumentProvider,
2589                 $exceptionHandler: $ExceptionHandlerProvider,
2590                 $filter: $FilterProvider,
2591                 $$forceReflow: $$ForceReflowProvider,
2592                 $interpolate: $InterpolateProvider,
2593                 $interval: $IntervalProvider,
2594                 $http: $HttpProvider,
2595                 $httpParamSerializer: $HttpParamSerializerProvider,
2596                 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2597                 $httpBackend: $HttpBackendProvider,
2598                 $xhrFactory: $xhrFactoryProvider,
2599                 $location: $LocationProvider,
2600                 $log: $LogProvider,
2601                 $parse: $ParseProvider,
2602                 $rootScope: $RootScopeProvider,
2603                 $q: $QProvider,
2604                 $$q: $$QProvider,
2605                 $sce: $SceProvider,
2606                 $sceDelegate: $SceDelegateProvider,
2607                 $sniffer: $SnifferProvider,
2608                 $templateCache: $TemplateCacheProvider,
2609                 $templateRequest: $TemplateRequestProvider,
2610                 $$testability: $$TestabilityProvider,
2611                 $timeout: $TimeoutProvider,
2612                 $window: $WindowProvider,
2613                 $$rAF: $$RAFProvider,
2614                 $$jqLite: $$jqLiteProvider,
2615                 $$HashMap: $$HashMapProvider,
2616                 $$cookieReader: $$CookieReaderProvider
2617               });
2618             }
2619           ]);
2620         }
2621
2622         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2623          *     Any commits to this file should be reviewed with security in mind.  *
2624          *   Changes to this file can potentially create security vulnerabilities. *
2625          *          An approval from 2 Core members with history of modifying      *
2626          *                         this file is required.                          *
2627          *                                                                         *
2628          *  Does the change somehow allow for arbitrary javascript to be executed? *
2629          *    Or allows for someone to change the prototype of built-in objects?   *
2630          *     Or gives undesired access to variables likes document or window?    *
2631          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2632
2633         /* global JQLitePrototype: true,
2634           addEventListenerFn: true,
2635           removeEventListenerFn: true,
2636           BOOLEAN_ATTR: true,
2637           ALIASED_ATTR: true,
2638         */
2639
2640         //////////////////////////////////
2641         //JQLite
2642         //////////////////////////////////
2643
2644         /**
2645          * @ngdoc function
2646          * @name angular.element
2647          * @module ng
2648          * @kind function
2649          *
2650          * @description
2651          * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2652          *
2653          * If jQuery is available, `angular.element` is an alias for the
2654          * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2655          * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2656          *
2657          * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2658          * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2659          * commonly needed functionality with the goal of having a very small footprint.</div>
2660          *
2661          * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2662          *
2663          * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2664          * jqLite; they are never raw DOM references.</div>
2665          *
2666          * ## Angular's jqLite
2667          * jqLite provides only the following jQuery methods:
2668          *
2669          * - [`addClass()`](http://api.jquery.com/addClass/)
2670          * - [`after()`](http://api.jquery.com/after/)
2671          * - [`append()`](http://api.jquery.com/append/)
2672          * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2673          * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2674          * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2675          * - [`clone()`](http://api.jquery.com/clone/)
2676          * - [`contents()`](http://api.jquery.com/contents/)
2677          * - [`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'.
2678          * - [`data()`](http://api.jquery.com/data/)
2679          * - [`detach()`](http://api.jquery.com/detach/)
2680          * - [`empty()`](http://api.jquery.com/empty/)
2681          * - [`eq()`](http://api.jquery.com/eq/)
2682          * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2683          * - [`hasClass()`](http://api.jquery.com/hasClass/)
2684          * - [`html()`](http://api.jquery.com/html/)
2685          * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2686          * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2687          * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2688          * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2689          * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2690          * - [`prepend()`](http://api.jquery.com/prepend/)
2691          * - [`prop()`](http://api.jquery.com/prop/)
2692          * - [`ready()`](http://api.jquery.com/ready/)
2693          * - [`remove()`](http://api.jquery.com/remove/)
2694          * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2695          * - [`removeClass()`](http://api.jquery.com/removeClass/)
2696          * - [`removeData()`](http://api.jquery.com/removeData/)
2697          * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2698          * - [`text()`](http://api.jquery.com/text/)
2699          * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2700          * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2701          * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2702          * - [`val()`](http://api.jquery.com/val/)
2703          * - [`wrap()`](http://api.jquery.com/wrap/)
2704          *
2705          * ## jQuery/jqLite Extras
2706          * Angular also provides the following additional methods and events to both jQuery and jqLite:
2707          *
2708          * ### Events
2709          * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2710          *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2711          *    element before it is removed.
2712          *
2713          * ### Methods
2714          * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2715          *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2716          *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2717          *   `'ngModel'`).
2718          * - `injector()` - retrieves the injector of the current element or its parent.
2719          * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2720          *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2721          *   be enabled.
2722          * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2723          *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2724          *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2725          *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2726          * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2727          *   parent element is reached.
2728          *
2729          * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2730          * @returns {Object} jQuery object.
2731          */
2732
2733         JQLite.expando = 'ng339';
2734
2735         var jqCache = JQLite.cache = {},
2736             jqId = 1,
2737             addEventListenerFn = function(element, type, fn) {
2738               element.addEventListener(type, fn, false);
2739             },
2740             removeEventListenerFn = function(element, type, fn) {
2741               element.removeEventListener(type, fn, false);
2742             };
2743
2744         /*
2745          * !!! This is an undocumented "private" function !!!
2746          */
2747         JQLite._data = function(node) {
2748           //jQuery always returns an object on cache miss
2749           return this.cache[node[this.expando]] || {};
2750         };
2751
2752         function jqNextId() { return ++jqId; }
2753
2754
2755         var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2756         var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2757         var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2758         var jqLiteMinErr = minErr('jqLite');
2759
2760         /**
2761          * Converts snake_case to camelCase.
2762          * Also there is special case for Moz prefix starting with upper case letter.
2763          * @param name Name to normalize
2764          */
2765         function camelCase(name) {
2766           return name.
2767             replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2768               return offset ? letter.toUpperCase() : letter;
2769             }).
2770             replace(MOZ_HACK_REGEXP, 'Moz$1');
2771         }
2772
2773         var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2774         var HTML_REGEXP = /<|&#?\w+;/;
2775         var TAG_NAME_REGEXP = /<([\w:-]+)/;
2776         var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2777
2778         var wrapMap = {
2779           'option': [1, '<select multiple="multiple">', '</select>'],
2780
2781           'thead': [1, '<table>', '</table>'],
2782           'col': [2, '<table><colgroup>', '</colgroup></table>'],
2783           'tr': [2, '<table><tbody>', '</tbody></table>'],
2784           'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2785           '_default': [0, "", ""]
2786         };
2787
2788         wrapMap.optgroup = wrapMap.option;
2789         wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2790         wrapMap.th = wrapMap.td;
2791
2792
2793         function jqLiteIsTextNode(html) {
2794           return !HTML_REGEXP.test(html);
2795         }
2796
2797         function jqLiteAcceptsData(node) {
2798           // The window object can accept data but has no nodeType
2799           // Otherwise we are only interested in elements (1) and documents (9)
2800           var nodeType = node.nodeType;
2801           return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2802         }
2803
2804         function jqLiteHasData(node) {
2805           for (var key in jqCache[node.ng339]) {
2806             return true;
2807           }
2808           return false;
2809         }
2810
2811         function jqLiteBuildFragment(html, context) {
2812           var tmp, tag, wrap,
2813               fragment = context.createDocumentFragment(),
2814               nodes = [], i;
2815
2816           if (jqLiteIsTextNode(html)) {
2817             // Convert non-html into a text node
2818             nodes.push(context.createTextNode(html));
2819           } else {
2820             // Convert html into DOM nodes
2821             tmp = tmp || fragment.appendChild(context.createElement("div"));
2822             tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2823             wrap = wrapMap[tag] || wrapMap._default;
2824             tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2825
2826             // Descend through wrappers to the right content
2827             i = wrap[0];
2828             while (i--) {
2829               tmp = tmp.lastChild;
2830             }
2831
2832             nodes = concat(nodes, tmp.childNodes);
2833
2834             tmp = fragment.firstChild;
2835             tmp.textContent = "";
2836           }
2837
2838           // Remove wrapper from fragment
2839           fragment.textContent = "";
2840           fragment.innerHTML = ""; // Clear inner HTML
2841           forEach(nodes, function(node) {
2842             fragment.appendChild(node);
2843           });
2844
2845           return fragment;
2846         }
2847
2848         function jqLiteParseHTML(html, context) {
2849           context = context || document;
2850           var parsed;
2851
2852           if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2853             return [context.createElement(parsed[1])];
2854           }
2855
2856           if ((parsed = jqLiteBuildFragment(html, context))) {
2857             return parsed.childNodes;
2858           }
2859
2860           return [];
2861         }
2862
2863
2864         // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2865         var jqLiteContains = Node.prototype.contains || function(arg) {
2866           // jshint bitwise: false
2867           return !!(this.compareDocumentPosition(arg) & 16);
2868           // jshint bitwise: true
2869         };
2870
2871         /////////////////////////////////////////////
2872         function JQLite(element) {
2873           if (element instanceof JQLite) {
2874             return element;
2875           }
2876
2877           var argIsString;
2878
2879           if (isString(element)) {
2880             element = trim(element);
2881             argIsString = true;
2882           }
2883           if (!(this instanceof JQLite)) {
2884             if (argIsString && element.charAt(0) != '<') {
2885               throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2886             }
2887             return new JQLite(element);
2888           }
2889
2890           if (argIsString) {
2891             jqLiteAddNodes(this, jqLiteParseHTML(element));
2892           } else {
2893             jqLiteAddNodes(this, element);
2894           }
2895         }
2896
2897         function jqLiteClone(element) {
2898           return element.cloneNode(true);
2899         }
2900
2901         function jqLiteDealoc(element, onlyDescendants) {
2902           if (!onlyDescendants) jqLiteRemoveData(element);
2903
2904           if (element.querySelectorAll) {
2905             var descendants = element.querySelectorAll('*');
2906             for (var i = 0, l = descendants.length; i < l; i++) {
2907               jqLiteRemoveData(descendants[i]);
2908             }
2909           }
2910         }
2911
2912         function jqLiteOff(element, type, fn, unsupported) {
2913           if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2914
2915           var expandoStore = jqLiteExpandoStore(element);
2916           var events = expandoStore && expandoStore.events;
2917           var handle = expandoStore && expandoStore.handle;
2918
2919           if (!handle) return; //no listeners registered
2920
2921           if (!type) {
2922             for (type in events) {
2923               if (type !== '$destroy') {
2924                 removeEventListenerFn(element, type, handle);
2925               }
2926               delete events[type];
2927             }
2928           } else {
2929
2930             var removeHandler = function(type) {
2931               var listenerFns = events[type];
2932               if (isDefined(fn)) {
2933                 arrayRemove(listenerFns || [], fn);
2934               }
2935               if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2936                 removeEventListenerFn(element, type, handle);
2937                 delete events[type];
2938               }
2939             };
2940
2941             forEach(type.split(' '), function(type) {
2942               removeHandler(type);
2943               if (MOUSE_EVENT_MAP[type]) {
2944                 removeHandler(MOUSE_EVENT_MAP[type]);
2945               }
2946             });
2947           }
2948         }
2949
2950         function jqLiteRemoveData(element, name) {
2951           var expandoId = element.ng339;
2952           var expandoStore = expandoId && jqCache[expandoId];
2953
2954           if (expandoStore) {
2955             if (name) {
2956               delete expandoStore.data[name];
2957               return;
2958             }
2959
2960             if (expandoStore.handle) {
2961               if (expandoStore.events.$destroy) {
2962                 expandoStore.handle({}, '$destroy');
2963               }
2964               jqLiteOff(element);
2965             }
2966             delete jqCache[expandoId];
2967             element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2968           }
2969         }
2970
2971
2972         function jqLiteExpandoStore(element, createIfNecessary) {
2973           var expandoId = element.ng339,
2974               expandoStore = expandoId && jqCache[expandoId];
2975
2976           if (createIfNecessary && !expandoStore) {
2977             element.ng339 = expandoId = jqNextId();
2978             expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2979           }
2980
2981           return expandoStore;
2982         }
2983
2984
2985         function jqLiteData(element, key, value) {
2986           if (jqLiteAcceptsData(element)) {
2987
2988             var isSimpleSetter = isDefined(value);
2989             var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2990             var massGetter = !key;
2991             var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2992             var data = expandoStore && expandoStore.data;
2993
2994             if (isSimpleSetter) { // data('key', value)
2995               data[key] = value;
2996             } else {
2997               if (massGetter) {  // data()
2998                 return data;
2999               } else {
3000                 if (isSimpleGetter) { // data('key')
3001                   // don't force creation of expandoStore if it doesn't exist yet
3002                   return data && data[key];
3003                 } else { // mass-setter: data({key1: val1, key2: val2})
3004                   extend(data, key);
3005                 }
3006               }
3007             }
3008           }
3009         }
3010
3011         function jqLiteHasClass(element, selector) {
3012           if (!element.getAttribute) return false;
3013           return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3014               indexOf(" " + selector + " ") > -1);
3015         }
3016
3017         function jqLiteRemoveClass(element, cssClasses) {
3018           if (cssClasses && element.setAttribute) {
3019             forEach(cssClasses.split(' '), function(cssClass) {
3020               element.setAttribute('class', trim(
3021                   (" " + (element.getAttribute('class') || '') + " ")
3022                   .replace(/[\n\t]/g, " ")
3023                   .replace(" " + trim(cssClass) + " ", " "))
3024               );
3025             });
3026           }
3027         }
3028
3029         function jqLiteAddClass(element, cssClasses) {
3030           if (cssClasses && element.setAttribute) {
3031             var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3032                                     .replace(/[\n\t]/g, " ");
3033
3034             forEach(cssClasses.split(' '), function(cssClass) {
3035               cssClass = trim(cssClass);
3036               if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3037                 existingClasses += cssClass + ' ';
3038               }
3039             });
3040
3041             element.setAttribute('class', trim(existingClasses));
3042           }
3043         }
3044
3045
3046         function jqLiteAddNodes(root, elements) {
3047           // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3048
3049           if (elements) {
3050
3051             // if a Node (the most common case)
3052             if (elements.nodeType) {
3053               root[root.length++] = elements;
3054             } else {
3055               var length = elements.length;
3056
3057               // if an Array or NodeList and not a Window
3058               if (typeof length === 'number' && elements.window !== elements) {
3059                 if (length) {
3060                   for (var i = 0; i < length; i++) {
3061                     root[root.length++] = elements[i];
3062                   }
3063                 }
3064               } else {
3065                 root[root.length++] = elements;
3066               }
3067             }
3068           }
3069         }
3070
3071
3072         function jqLiteController(element, name) {
3073           return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3074         }
3075
3076         function jqLiteInheritedData(element, name, value) {
3077           // if element is the document object work with the html element instead
3078           // this makes $(document).scope() possible
3079           if (element.nodeType == NODE_TYPE_DOCUMENT) {
3080             element = element.documentElement;
3081           }
3082           var names = isArray(name) ? name : [name];
3083
3084           while (element) {
3085             for (var i = 0, ii = names.length; i < ii; i++) {
3086               if (isDefined(value = jqLite.data(element, names[i]))) return value;
3087             }
3088
3089             // If dealing with a document fragment node with a host element, and no parent, use the host
3090             // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3091             // to lookup parent controllers.
3092             element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3093           }
3094         }
3095
3096         function jqLiteEmpty(element) {
3097           jqLiteDealoc(element, true);
3098           while (element.firstChild) {
3099             element.removeChild(element.firstChild);
3100           }
3101         }
3102
3103         function jqLiteRemove(element, keepData) {
3104           if (!keepData) jqLiteDealoc(element);
3105           var parent = element.parentNode;
3106           if (parent) parent.removeChild(element);
3107         }
3108
3109
3110         function jqLiteDocumentLoaded(action, win) {
3111           win = win || window;
3112           if (win.document.readyState === 'complete') {
3113             // Force the action to be run async for consistent behaviour
3114             // from the action's point of view
3115             // i.e. it will definitely not be in a $apply
3116             win.setTimeout(action);
3117           } else {
3118             // No need to unbind this handler as load is only ever called once
3119             jqLite(win).on('load', action);
3120           }
3121         }
3122
3123         //////////////////////////////////////////
3124         // Functions which are declared directly.
3125         //////////////////////////////////////////
3126         var JQLitePrototype = JQLite.prototype = {
3127           ready: function(fn) {
3128             var fired = false;
3129
3130             function trigger() {
3131               if (fired) return;
3132               fired = true;
3133               fn();
3134             }
3135
3136             // check if document is already loaded
3137             if (document.readyState === 'complete') {
3138               setTimeout(trigger);
3139             } else {
3140               this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3141               // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3142               // jshint -W064
3143               JQLite(window).on('load', trigger); // fallback to window.onload for others
3144               // jshint +W064
3145             }
3146           },
3147           toString: function() {
3148             var value = [];
3149             forEach(this, function(e) { value.push('' + e);});
3150             return '[' + value.join(', ') + ']';
3151           },
3152
3153           eq: function(index) {
3154               return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3155           },
3156
3157           length: 0,
3158           push: push,
3159           sort: [].sort,
3160           splice: [].splice
3161         };
3162
3163         //////////////////////////////////////////
3164         // Functions iterating getter/setters.
3165         // these functions return self on setter and
3166         // value on get.
3167         //////////////////////////////////////////
3168         var BOOLEAN_ATTR = {};
3169         forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3170           BOOLEAN_ATTR[lowercase(value)] = value;
3171         });
3172         var BOOLEAN_ELEMENTS = {};
3173         forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3174           BOOLEAN_ELEMENTS[value] = true;
3175         });
3176         var ALIASED_ATTR = {
3177           'ngMinlength': 'minlength',
3178           'ngMaxlength': 'maxlength',
3179           'ngMin': 'min',
3180           'ngMax': 'max',
3181           'ngPattern': 'pattern'
3182         };
3183
3184         function getBooleanAttrName(element, name) {
3185           // check dom last since we will most likely fail on name
3186           var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3187
3188           // booleanAttr is here twice to minimize DOM access
3189           return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3190         }
3191
3192         function getAliasedAttrName(name) {
3193           return ALIASED_ATTR[name];
3194         }
3195
3196         forEach({
3197           data: jqLiteData,
3198           removeData: jqLiteRemoveData,
3199           hasData: jqLiteHasData
3200         }, function(fn, name) {
3201           JQLite[name] = fn;
3202         });
3203
3204         forEach({
3205           data: jqLiteData,
3206           inheritedData: jqLiteInheritedData,
3207
3208           scope: function(element) {
3209             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3210             return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3211           },
3212
3213           isolateScope: function(element) {
3214             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3215             return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3216           },
3217
3218           controller: jqLiteController,
3219
3220           injector: function(element) {
3221             return jqLiteInheritedData(element, '$injector');
3222           },
3223
3224           removeAttr: function(element, name) {
3225             element.removeAttribute(name);
3226           },
3227
3228           hasClass: jqLiteHasClass,
3229
3230           css: function(element, name, value) {
3231             name = camelCase(name);
3232
3233             if (isDefined(value)) {
3234               element.style[name] = value;
3235             } else {
3236               return element.style[name];
3237             }
3238           },
3239
3240           attr: function(element, name, value) {
3241             var nodeType = element.nodeType;
3242             if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3243               return;
3244             }
3245             var lowercasedName = lowercase(name);
3246             if (BOOLEAN_ATTR[lowercasedName]) {
3247               if (isDefined(value)) {
3248                 if (!!value) {
3249                   element[name] = true;
3250                   element.setAttribute(name, lowercasedName);
3251                 } else {
3252                   element[name] = false;
3253                   element.removeAttribute(lowercasedName);
3254                 }
3255               } else {
3256                 return (element[name] ||
3257                          (element.attributes.getNamedItem(name) || noop).specified)
3258                        ? lowercasedName
3259                        : undefined;
3260               }
3261             } else if (isDefined(value)) {
3262               element.setAttribute(name, value);
3263             } else if (element.getAttribute) {
3264               // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3265               // some elements (e.g. Document) don't have get attribute, so return undefined
3266               var ret = element.getAttribute(name, 2);
3267               // normalize non-existing attributes to undefined (as jQuery)
3268               return ret === null ? undefined : ret;
3269             }
3270           },
3271
3272           prop: function(element, name, value) {
3273             if (isDefined(value)) {
3274               element[name] = value;
3275             } else {
3276               return element[name];
3277             }
3278           },
3279
3280           text: (function() {
3281             getText.$dv = '';
3282             return getText;
3283
3284             function getText(element, value) {
3285               if (isUndefined(value)) {
3286                 var nodeType = element.nodeType;
3287                 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3288               }
3289               element.textContent = value;
3290             }
3291           })(),
3292
3293           val: function(element, value) {
3294             if (isUndefined(value)) {
3295               if (element.multiple && nodeName_(element) === 'select') {
3296                 var result = [];
3297                 forEach(element.options, function(option) {
3298                   if (option.selected) {
3299                     result.push(option.value || option.text);
3300                   }
3301                 });
3302                 return result.length === 0 ? null : result;
3303               }
3304               return element.value;
3305             }
3306             element.value = value;
3307           },
3308
3309           html: function(element, value) {
3310             if (isUndefined(value)) {
3311               return element.innerHTML;
3312             }
3313             jqLiteDealoc(element, true);
3314             element.innerHTML = value;
3315           },
3316
3317           empty: jqLiteEmpty
3318         }, function(fn, name) {
3319           /**
3320            * Properties: writes return selection, reads return first value
3321            */
3322           JQLite.prototype[name] = function(arg1, arg2) {
3323             var i, key;
3324             var nodeCount = this.length;
3325
3326             // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3327             // in a way that survives minification.
3328             // jqLiteEmpty takes no arguments but is a setter.
3329             if (fn !== jqLiteEmpty &&
3330                 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3331               if (isObject(arg1)) {
3332
3333                 // we are a write, but the object properties are the key/values
3334                 for (i = 0; i < nodeCount; i++) {
3335                   if (fn === jqLiteData) {
3336                     // data() takes the whole object in jQuery
3337                     fn(this[i], arg1);
3338                   } else {
3339                     for (key in arg1) {
3340                       fn(this[i], key, arg1[key]);
3341                     }
3342                   }
3343                 }
3344                 // return self for chaining
3345                 return this;
3346               } else {
3347                 // we are a read, so read the first child.
3348                 // TODO: do we still need this?
3349                 var value = fn.$dv;
3350                 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3351                 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3352                 for (var j = 0; j < jj; j++) {
3353                   var nodeValue = fn(this[j], arg1, arg2);
3354                   value = value ? value + nodeValue : nodeValue;
3355                 }
3356                 return value;
3357               }
3358             } else {
3359               // we are a write, so apply to all children
3360               for (i = 0; i < nodeCount; i++) {
3361                 fn(this[i], arg1, arg2);
3362               }
3363               // return self for chaining
3364               return this;
3365             }
3366           };
3367         });
3368
3369         function createEventHandler(element, events) {
3370           var eventHandler = function(event, type) {
3371             // jQuery specific api
3372             event.isDefaultPrevented = function() {
3373               return event.defaultPrevented;
3374             };
3375
3376             var eventFns = events[type || event.type];
3377             var eventFnsLength = eventFns ? eventFns.length : 0;
3378
3379             if (!eventFnsLength) return;
3380
3381             if (isUndefined(event.immediatePropagationStopped)) {
3382               var originalStopImmediatePropagation = event.stopImmediatePropagation;
3383               event.stopImmediatePropagation = function() {
3384                 event.immediatePropagationStopped = true;
3385
3386                 if (event.stopPropagation) {
3387                   event.stopPropagation();
3388                 }
3389
3390                 if (originalStopImmediatePropagation) {
3391                   originalStopImmediatePropagation.call(event);
3392                 }
3393               };
3394             }
3395
3396             event.isImmediatePropagationStopped = function() {
3397               return event.immediatePropagationStopped === true;
3398             };
3399
3400             // Some events have special handlers that wrap the real handler
3401             var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3402
3403             // Copy event handlers in case event handlers array is modified during execution.
3404             if ((eventFnsLength > 1)) {
3405               eventFns = shallowCopy(eventFns);
3406             }
3407
3408             for (var i = 0; i < eventFnsLength; i++) {
3409               if (!event.isImmediatePropagationStopped()) {
3410                 handlerWrapper(element, event, eventFns[i]);
3411               }
3412             }
3413           };
3414
3415           // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3416           //       events on `element`
3417           eventHandler.elem = element;
3418           return eventHandler;
3419         }
3420
3421         function defaultHandlerWrapper(element, event, handler) {
3422           handler.call(element, event);
3423         }
3424
3425         function specialMouseHandlerWrapper(target, event, handler) {
3426           // Refer to jQuery's implementation of mouseenter & mouseleave
3427           // Read about mouseenter and mouseleave:
3428           // http://www.quirksmode.org/js/events_mouse.html#link8
3429           var related = event.relatedTarget;
3430           // For mousenter/leave call the handler if related is outside the target.
3431           // NB: No relatedTarget if the mouse left/entered the browser window
3432           if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3433             handler.call(target, event);
3434           }
3435         }
3436
3437         //////////////////////////////////////////
3438         // Functions iterating traversal.
3439         // These functions chain results into a single
3440         // selector.
3441         //////////////////////////////////////////
3442         forEach({
3443           removeData: jqLiteRemoveData,
3444
3445           on: function jqLiteOn(element, type, fn, unsupported) {
3446             if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3447
3448             // Do not add event handlers to non-elements because they will not be cleaned up.
3449             if (!jqLiteAcceptsData(element)) {
3450               return;
3451             }
3452
3453             var expandoStore = jqLiteExpandoStore(element, true);
3454             var events = expandoStore.events;
3455             var handle = expandoStore.handle;
3456
3457             if (!handle) {
3458               handle = expandoStore.handle = createEventHandler(element, events);
3459             }
3460
3461             // http://jsperf.com/string-indexof-vs-split
3462             var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3463             var i = types.length;
3464
3465             var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3466               var eventFns = events[type];
3467
3468               if (!eventFns) {
3469                 eventFns = events[type] = [];
3470                 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3471                 if (type !== '$destroy' && !noEventListener) {
3472                   addEventListenerFn(element, type, handle);
3473                 }
3474               }
3475
3476               eventFns.push(fn);
3477             };
3478
3479             while (i--) {
3480               type = types[i];
3481               if (MOUSE_EVENT_MAP[type]) {
3482                 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3483                 addHandler(type, undefined, true);
3484               } else {
3485                 addHandler(type);
3486               }
3487             }
3488           },
3489
3490           off: jqLiteOff,
3491
3492           one: function(element, type, fn) {
3493             element = jqLite(element);
3494
3495             //add the listener twice so that when it is called
3496             //you can remove the original function and still be
3497             //able to call element.off(ev, fn) normally
3498             element.on(type, function onFn() {
3499               element.off(type, fn);
3500               element.off(type, onFn);
3501             });
3502             element.on(type, fn);
3503           },
3504
3505           replaceWith: function(element, replaceNode) {
3506             var index, parent = element.parentNode;
3507             jqLiteDealoc(element);
3508             forEach(new JQLite(replaceNode), function(node) {
3509               if (index) {
3510                 parent.insertBefore(node, index.nextSibling);
3511               } else {
3512                 parent.replaceChild(node, element);
3513               }
3514               index = node;
3515             });
3516           },
3517
3518           children: function(element) {
3519             var children = [];
3520             forEach(element.childNodes, function(element) {
3521               if (element.nodeType === NODE_TYPE_ELEMENT) {
3522                 children.push(element);
3523               }
3524             });
3525             return children;
3526           },
3527
3528           contents: function(element) {
3529             return element.contentDocument || element.childNodes || [];
3530           },
3531
3532           append: function(element, node) {
3533             var nodeType = element.nodeType;
3534             if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3535
3536             node = new JQLite(node);
3537
3538             for (var i = 0, ii = node.length; i < ii; i++) {
3539               var child = node[i];
3540               element.appendChild(child);
3541             }
3542           },
3543
3544           prepend: function(element, node) {
3545             if (element.nodeType === NODE_TYPE_ELEMENT) {
3546               var index = element.firstChild;
3547               forEach(new JQLite(node), function(child) {
3548                 element.insertBefore(child, index);
3549               });
3550             }
3551           },
3552
3553           wrap: function(element, wrapNode) {
3554             wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3555             var parent = element.parentNode;
3556             if (parent) {
3557               parent.replaceChild(wrapNode, element);
3558             }
3559             wrapNode.appendChild(element);
3560           },
3561
3562           remove: jqLiteRemove,
3563
3564           detach: function(element) {
3565             jqLiteRemove(element, true);
3566           },
3567
3568           after: function(element, newElement) {
3569             var index = element, parent = element.parentNode;
3570             newElement = new JQLite(newElement);
3571
3572             for (var i = 0, ii = newElement.length; i < ii; i++) {
3573               var node = newElement[i];
3574               parent.insertBefore(node, index.nextSibling);
3575               index = node;
3576             }
3577           },
3578
3579           addClass: jqLiteAddClass,
3580           removeClass: jqLiteRemoveClass,
3581
3582           toggleClass: function(element, selector, condition) {
3583             if (selector) {
3584               forEach(selector.split(' '), function(className) {
3585                 var classCondition = condition;
3586                 if (isUndefined(classCondition)) {
3587                   classCondition = !jqLiteHasClass(element, className);
3588                 }
3589                 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3590               });
3591             }
3592           },
3593
3594           parent: function(element) {
3595             var parent = element.parentNode;
3596             return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3597           },
3598
3599           next: function(element) {
3600             return element.nextElementSibling;
3601           },
3602
3603           find: function(element, selector) {
3604             if (element.getElementsByTagName) {
3605               return element.getElementsByTagName(selector);
3606             } else {
3607               return [];
3608             }
3609           },
3610
3611           clone: jqLiteClone,
3612
3613           triggerHandler: function(element, event, extraParameters) {
3614
3615             var dummyEvent, eventFnsCopy, handlerArgs;
3616             var eventName = event.type || event;
3617             var expandoStore = jqLiteExpandoStore(element);
3618             var events = expandoStore && expandoStore.events;
3619             var eventFns = events && events[eventName];
3620
3621             if (eventFns) {
3622               // Create a dummy event to pass to the handlers
3623               dummyEvent = {
3624                 preventDefault: function() { this.defaultPrevented = true; },
3625                 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3626                 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3627                 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3628                 stopPropagation: noop,
3629                 type: eventName,
3630                 target: element
3631               };
3632
3633               // If a custom event was provided then extend our dummy event with it
3634               if (event.type) {
3635                 dummyEvent = extend(dummyEvent, event);
3636               }
3637
3638               // Copy event handlers in case event handlers array is modified during execution.
3639               eventFnsCopy = shallowCopy(eventFns);
3640               handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3641
3642               forEach(eventFnsCopy, function(fn) {
3643                 if (!dummyEvent.isImmediatePropagationStopped()) {
3644                   fn.apply(element, handlerArgs);
3645                 }
3646               });
3647             }
3648           }
3649         }, function(fn, name) {
3650           /**
3651            * chaining functions
3652            */
3653           JQLite.prototype[name] = function(arg1, arg2, arg3) {
3654             var value;
3655
3656             for (var i = 0, ii = this.length; i < ii; i++) {
3657               if (isUndefined(value)) {
3658                 value = fn(this[i], arg1, arg2, arg3);
3659                 if (isDefined(value)) {
3660                   // any function which returns a value needs to be wrapped
3661                   value = jqLite(value);
3662                 }
3663               } else {
3664                 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3665               }
3666             }
3667             return isDefined(value) ? value : this;
3668           };
3669
3670           // bind legacy bind/unbind to on/off
3671           JQLite.prototype.bind = JQLite.prototype.on;
3672           JQLite.prototype.unbind = JQLite.prototype.off;
3673         });
3674
3675
3676         // Provider for private $$jqLite service
3677         function $$jqLiteProvider() {
3678           this.$get = function $$jqLite() {
3679             return extend(JQLite, {
3680               hasClass: function(node, classes) {
3681                 if (node.attr) node = node[0];
3682                 return jqLiteHasClass(node, classes);
3683               },
3684               addClass: function(node, classes) {
3685                 if (node.attr) node = node[0];
3686                 return jqLiteAddClass(node, classes);
3687               },
3688               removeClass: function(node, classes) {
3689                 if (node.attr) node = node[0];
3690                 return jqLiteRemoveClass(node, classes);
3691               }
3692             });
3693           };
3694         }
3695
3696         /**
3697          * Computes a hash of an 'obj'.
3698          * Hash of a:
3699          *  string is string
3700          *  number is number as string
3701          *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3702          *         that is also assigned to the $$hashKey property of the object.
3703          *
3704          * @param obj
3705          * @returns {string} hash string such that the same input will have the same hash string.
3706          *         The resulting string key is in 'type:hashKey' format.
3707          */
3708         function hashKey(obj, nextUidFn) {
3709           var key = obj && obj.$$hashKey;
3710
3711           if (key) {
3712             if (typeof key === 'function') {
3713               key = obj.$$hashKey();
3714             }
3715             return key;
3716           }
3717
3718           var objType = typeof obj;
3719           if (objType == 'function' || (objType == 'object' && obj !== null)) {
3720             key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3721           } else {
3722             key = objType + ':' + obj;
3723           }
3724
3725           return key;
3726         }
3727
3728         /**
3729          * HashMap which can use objects as keys
3730          */
3731         function HashMap(array, isolatedUid) {
3732           if (isolatedUid) {
3733             var uid = 0;
3734             this.nextUid = function() {
3735               return ++uid;
3736             };
3737           }
3738           forEach(array, this.put, this);
3739         }
3740         HashMap.prototype = {
3741           /**
3742            * Store key value pair
3743            * @param key key to store can be any type
3744            * @param value value to store can be any type
3745            */
3746           put: function(key, value) {
3747             this[hashKey(key, this.nextUid)] = value;
3748           },
3749
3750           /**
3751            * @param key
3752            * @returns {Object} the value for the key
3753            */
3754           get: function(key) {
3755             return this[hashKey(key, this.nextUid)];
3756           },
3757
3758           /**
3759            * Remove the key/value pair
3760            * @param key
3761            */
3762           remove: function(key) {
3763             var value = this[key = hashKey(key, this.nextUid)];
3764             delete this[key];
3765             return value;
3766           }
3767         };
3768
3769         var $$HashMapProvider = [function() {
3770           this.$get = [function() {
3771             return HashMap;
3772           }];
3773         }];
3774
3775         /**
3776          * @ngdoc function
3777          * @module ng
3778          * @name angular.injector
3779          * @kind function
3780          *
3781          * @description
3782          * Creates an injector object that can be used for retrieving services as well as for
3783          * dependency injection (see {@link guide/di dependency injection}).
3784          *
3785          * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3786          *     {@link angular.module}. The `ng` module must be explicitly added.
3787          * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3788          *     disallows argument name annotation inference.
3789          * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3790          *
3791          * @example
3792          * Typical usage
3793          * ```js
3794          *   // create an injector
3795          *   var $injector = angular.injector(['ng']);
3796          *
3797          *   // use the injector to kick off your application
3798          *   // use the type inference to auto inject arguments, or use implicit injection
3799          *   $injector.invoke(function($rootScope, $compile, $document) {
3800          *     $compile($document)($rootScope);
3801          *     $rootScope.$digest();
3802          *   });
3803          * ```
3804          *
3805          * Sometimes you want to get access to the injector of a currently running Angular app
3806          * from outside Angular. Perhaps, you want to inject and compile some markup after the
3807          * application has been bootstrapped. You can do this using the extra `injector()` added
3808          * to JQuery/jqLite elements. See {@link angular.element}.
3809          *
3810          * *This is fairly rare but could be the case if a third party library is injecting the
3811          * markup.*
3812          *
3813          * In the following example a new block of HTML containing a `ng-controller`
3814          * directive is added to the end of the document body by JQuery. We then compile and link
3815          * it into the current AngularJS scope.
3816          *
3817          * ```js
3818          * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3819          * $(document.body).append($div);
3820          *
3821          * angular.element(document).injector().invoke(function($compile) {
3822          *   var scope = angular.element($div).scope();
3823          *   $compile($div)(scope);
3824          * });
3825          * ```
3826          */
3827
3828
3829         /**
3830          * @ngdoc module
3831          * @name auto
3832          * @description
3833          *
3834          * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3835          */
3836
3837         var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3838         var FN_ARG_SPLIT = /,/;
3839         var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3840         var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3841         var $injectorMinErr = minErr('$injector');
3842
3843         function anonFn(fn) {
3844           // For anonymous functions, showing at the very least the function signature can help in
3845           // debugging.
3846           var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3847               args = fnText.match(FN_ARGS);
3848           if (args) {
3849             return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3850           }
3851           return 'fn';
3852         }
3853
3854         function annotate(fn, strictDi, name) {
3855           var $inject,
3856               fnText,
3857               argDecl,
3858               last;
3859
3860           if (typeof fn === 'function') {
3861             if (!($inject = fn.$inject)) {
3862               $inject = [];
3863               if (fn.length) {
3864                 if (strictDi) {
3865                   if (!isString(name) || !name) {
3866                     name = fn.name || anonFn(fn);
3867                   }
3868                   throw $injectorMinErr('strictdi',
3869                     '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3870                 }
3871                 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3872                 argDecl = fnText.match(FN_ARGS);
3873                 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3874                   arg.replace(FN_ARG, function(all, underscore, name) {
3875                     $inject.push(name);
3876                   });
3877                 });
3878               }
3879               fn.$inject = $inject;
3880             }
3881           } else if (isArray(fn)) {
3882             last = fn.length - 1;
3883             assertArgFn(fn[last], 'fn');
3884             $inject = fn.slice(0, last);
3885           } else {
3886             assertArgFn(fn, 'fn', true);
3887           }
3888           return $inject;
3889         }
3890
3891         ///////////////////////////////////////
3892
3893         /**
3894          * @ngdoc service
3895          * @name $injector
3896          *
3897          * @description
3898          *
3899          * `$injector` is used to retrieve object instances as defined by
3900          * {@link auto.$provide provider}, instantiate types, invoke methods,
3901          * and load modules.
3902          *
3903          * The following always holds true:
3904          *
3905          * ```js
3906          *   var $injector = angular.injector();
3907          *   expect($injector.get('$injector')).toBe($injector);
3908          *   expect($injector.invoke(function($injector) {
3909          *     return $injector;
3910          *   })).toBe($injector);
3911          * ```
3912          *
3913          * # Injection Function Annotation
3914          *
3915          * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3916          * following are all valid ways of annotating function with injection arguments and are equivalent.
3917          *
3918          * ```js
3919          *   // inferred (only works if code not minified/obfuscated)
3920          *   $injector.invoke(function(serviceA){});
3921          *
3922          *   // annotated
3923          *   function explicit(serviceA) {};
3924          *   explicit.$inject = ['serviceA'];
3925          *   $injector.invoke(explicit);
3926          *
3927          *   // inline
3928          *   $injector.invoke(['serviceA', function(serviceA){}]);
3929          * ```
3930          *
3931          * ## Inference
3932          *
3933          * In JavaScript calling `toString()` on a function returns the function definition. The definition
3934          * can then be parsed and the function arguments can be extracted. This method of discovering
3935          * annotations is disallowed when the injector is in strict mode.
3936          * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3937          * argument names.
3938          *
3939          * ## `$inject` Annotation
3940          * By adding an `$inject` property onto a function the injection parameters can be specified.
3941          *
3942          * ## Inline
3943          * As an array of injection names, where the last item in the array is the function to call.
3944          */
3945
3946         /**
3947          * @ngdoc method
3948          * @name $injector#get
3949          *
3950          * @description
3951          * Return an instance of the service.
3952          *
3953          * @param {string} name The name of the instance to retrieve.
3954          * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3955          * @return {*} The instance.
3956          */
3957
3958         /**
3959          * @ngdoc method
3960          * @name $injector#invoke
3961          *
3962          * @description
3963          * Invoke the method and supply the method arguments from the `$injector`.
3964          *
3965          * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3966          *   injected according to the {@link guide/di $inject Annotation} rules.
3967          * @param {Object=} self The `this` for the invoked method.
3968          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3969          *                         object first, before the `$injector` is consulted.
3970          * @returns {*} the value returned by the invoked `fn` function.
3971          */
3972
3973         /**
3974          * @ngdoc method
3975          * @name $injector#has
3976          *
3977          * @description
3978          * Allows the user to query if the particular service exists.
3979          *
3980          * @param {string} name Name of the service to query.
3981          * @returns {boolean} `true` if injector has given service.
3982          */
3983
3984         /**
3985          * @ngdoc method
3986          * @name $injector#instantiate
3987          * @description
3988          * Create a new instance of JS type. The method takes a constructor function, invokes the new
3989          * operator, and supplies all of the arguments to the constructor function as specified by the
3990          * constructor annotation.
3991          *
3992          * @param {Function} Type Annotated constructor function.
3993          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3994          * object first, before the `$injector` is consulted.
3995          * @returns {Object} new instance of `Type`.
3996          */
3997
3998         /**
3999          * @ngdoc method
4000          * @name $injector#annotate
4001          *
4002          * @description
4003          * Returns an array of service names which the function is requesting for injection. This API is
4004          * used by the injector to determine which services need to be injected into the function when the
4005          * function is invoked. There are three ways in which the function can be annotated with the needed
4006          * dependencies.
4007          *
4008          * # Argument names
4009          *
4010          * The simplest form is to extract the dependencies from the arguments of the function. This is done
4011          * by converting the function into a string using `toString()` method and extracting the argument
4012          * names.
4013          * ```js
4014          *   // Given
4015          *   function MyController($scope, $route) {
4016          *     // ...
4017          *   }
4018          *
4019          *   // Then
4020          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4021          * ```
4022          *
4023          * You can disallow this method by using strict injection mode.
4024          *
4025          * This method does not work with code minification / obfuscation. For this reason the following
4026          * annotation strategies are supported.
4027          *
4028          * # The `$inject` property
4029          *
4030          * If a function has an `$inject` property and its value is an array of strings, then the strings
4031          * represent names of services to be injected into the function.
4032          * ```js
4033          *   // Given
4034          *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4035          *     // ...
4036          *   }
4037          *   // Define function dependencies
4038          *   MyController['$inject'] = ['$scope', '$route'];
4039          *
4040          *   // Then
4041          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4042          * ```
4043          *
4044          * # The array notation
4045          *
4046          * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4047          * is very inconvenient. In these situations using the array notation to specify the dependencies in
4048          * a way that survives minification is a better choice:
4049          *
4050          * ```js
4051          *   // We wish to write this (not minification / obfuscation safe)
4052          *   injector.invoke(function($compile, $rootScope) {
4053          *     // ...
4054          *   });
4055          *
4056          *   // We are forced to write break inlining
4057          *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4058          *     // ...
4059          *   };
4060          *   tmpFn.$inject = ['$compile', '$rootScope'];
4061          *   injector.invoke(tmpFn);
4062          *
4063          *   // To better support inline function the inline annotation is supported
4064          *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4065          *     // ...
4066          *   }]);
4067          *
4068          *   // Therefore
4069          *   expect(injector.annotate(
4070          *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4071          *    ).toEqual(['$compile', '$rootScope']);
4072          * ```
4073          *
4074          * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4075          * be retrieved as described above.
4076          *
4077          * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4078          *
4079          * @returns {Array.<string>} The names of the services which the function requires.
4080          */
4081
4082
4083
4084
4085         /**
4086          * @ngdoc service
4087          * @name $provide
4088          *
4089          * @description
4090          *
4091          * The {@link auto.$provide $provide} service has a number of methods for registering components
4092          * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4093          * {@link angular.Module}.
4094          *
4095          * An Angular **service** is a singleton object created by a **service factory**.  These **service
4096          * factories** are functions which, in turn, are created by a **service provider**.
4097          * The **service providers** are constructor functions. When instantiated they must contain a
4098          * property called `$get`, which holds the **service factory** function.
4099          *
4100          * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4101          * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4102          * function to get the instance of the **service**.
4103          *
4104          * Often services have no configuration options and there is no need to add methods to the service
4105          * provider.  The provider will be no more than a constructor function with a `$get` property. For
4106          * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4107          * services without specifying a provider.
4108          *
4109          * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4110          *     {@link auto.$injector $injector}
4111          * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4112          *     providers and services.
4113          * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4114          *     services, not providers.
4115          * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4116          *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4117          *     given factory function.
4118          * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4119          *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4120          *      a new object using the given constructor function.
4121          *
4122          * See the individual methods for more information and examples.
4123          */
4124
4125         /**
4126          * @ngdoc method
4127          * @name $provide#provider
4128          * @description
4129          *
4130          * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4131          * are constructor functions, whose instances are responsible for "providing" a factory for a
4132          * service.
4133          *
4134          * Service provider names start with the name of the service they provide followed by `Provider`.
4135          * For example, the {@link ng.$log $log} service has a provider called
4136          * {@link ng.$logProvider $logProvider}.
4137          *
4138          * Service provider objects can have additional methods which allow configuration of the provider
4139          * and its service. Importantly, you can configure what kind of service is created by the `$get`
4140          * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4141          * method {@link ng.$logProvider#debugEnabled debugEnabled}
4142          * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4143          * console or not.
4144          *
4145          * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4146                                 'Provider'` key.
4147          * @param {(Object|function())} provider If the provider is:
4148          *
4149          *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4150          *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4151          *   - `Constructor`: a new instance of the provider will be created using
4152          *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4153          *
4154          * @returns {Object} registered provider instance
4155
4156          * @example
4157          *
4158          * The following example shows how to create a simple event tracking service and register it using
4159          * {@link auto.$provide#provider $provide.provider()}.
4160          *
4161          * ```js
4162          *  // Define the eventTracker provider
4163          *  function EventTrackerProvider() {
4164          *    var trackingUrl = '/track';
4165          *
4166          *    // A provider method for configuring where the tracked events should been saved
4167          *    this.setTrackingUrl = function(url) {
4168          *      trackingUrl = url;
4169          *    };
4170          *
4171          *    // The service factory function
4172          *    this.$get = ['$http', function($http) {
4173          *      var trackedEvents = {};
4174          *      return {
4175          *        // Call this to track an event
4176          *        event: function(event) {
4177          *          var count = trackedEvents[event] || 0;
4178          *          count += 1;
4179          *          trackedEvents[event] = count;
4180          *          return count;
4181          *        },
4182          *        // Call this to save the tracked events to the trackingUrl
4183          *        save: function() {
4184          *          $http.post(trackingUrl, trackedEvents);
4185          *        }
4186          *      };
4187          *    }];
4188          *  }
4189          *
4190          *  describe('eventTracker', function() {
4191          *    var postSpy;
4192          *
4193          *    beforeEach(module(function($provide) {
4194          *      // Register the eventTracker provider
4195          *      $provide.provider('eventTracker', EventTrackerProvider);
4196          *    }));
4197          *
4198          *    beforeEach(module(function(eventTrackerProvider) {
4199          *      // Configure eventTracker provider
4200          *      eventTrackerProvider.setTrackingUrl('/custom-track');
4201          *    }));
4202          *
4203          *    it('tracks events', inject(function(eventTracker) {
4204          *      expect(eventTracker.event('login')).toEqual(1);
4205          *      expect(eventTracker.event('login')).toEqual(2);
4206          *    }));
4207          *
4208          *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4209          *      postSpy = spyOn($http, 'post');
4210          *      eventTracker.event('login');
4211          *      eventTracker.save();
4212          *      expect(postSpy).toHaveBeenCalled();
4213          *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4214          *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4215          *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4216          *    }));
4217          *  });
4218          * ```
4219          */
4220
4221         /**
4222          * @ngdoc method
4223          * @name $provide#factory
4224          * @description
4225          *
4226          * Register a **service factory**, which will be called to return the service instance.
4227          * This is short for registering a service where its provider consists of only a `$get` property,
4228          * which is the given service factory function.
4229          * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4230          * configure your service in a provider.
4231          *
4232          * @param {string} name The name of the instance.
4233          * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4234          *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4235          * @returns {Object} registered provider instance
4236          *
4237          * @example
4238          * Here is an example of registering a service
4239          * ```js
4240          *   $provide.factory('ping', ['$http', function($http) {
4241          *     return function ping() {
4242          *       return $http.send('/ping');
4243          *     };
4244          *   }]);
4245          * ```
4246          * You would then inject and use this service like this:
4247          * ```js
4248          *   someModule.controller('Ctrl', ['ping', function(ping) {
4249          *     ping();
4250          *   }]);
4251          * ```
4252          */
4253
4254
4255         /**
4256          * @ngdoc method
4257          * @name $provide#service
4258          * @description
4259          *
4260          * Register a **service constructor**, which will be invoked with `new` to create the service
4261          * instance.
4262          * This is short for registering a service where its provider's `$get` property is the service
4263          * constructor function that will be used to instantiate the service instance.
4264          *
4265          * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4266          * as a type/class.
4267          *
4268          * @param {string} name The name of the instance.
4269          * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4270          *     that will be instantiated.
4271          * @returns {Object} registered provider instance
4272          *
4273          * @example
4274          * Here is an example of registering a service using
4275          * {@link auto.$provide#service $provide.service(class)}.
4276          * ```js
4277          *   var Ping = function($http) {
4278          *     this.$http = $http;
4279          *   };
4280          *
4281          *   Ping.$inject = ['$http'];
4282          *
4283          *   Ping.prototype.send = function() {
4284          *     return this.$http.get('/ping');
4285          *   };
4286          *   $provide.service('ping', Ping);
4287          * ```
4288          * You would then inject and use this service like this:
4289          * ```js
4290          *   someModule.controller('Ctrl', ['ping', function(ping) {
4291          *     ping.send();
4292          *   }]);
4293          * ```
4294          */
4295
4296
4297         /**
4298          * @ngdoc method
4299          * @name $provide#value
4300          * @description
4301          *
4302          * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4303          * number, an array, an object or a function.  This is short for registering a service where its
4304          * provider's `$get` property is a factory function that takes no arguments and returns the **value
4305          * service**.
4306          *
4307          * Value services are similar to constant services, except that they cannot be injected into a
4308          * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4309          * an Angular
4310          * {@link auto.$provide#decorator decorator}.
4311          *
4312          * @param {string} name The name of the instance.
4313          * @param {*} value The value.
4314          * @returns {Object} registered provider instance
4315          *
4316          * @example
4317          * Here are some examples of creating value services.
4318          * ```js
4319          *   $provide.value('ADMIN_USER', 'admin');
4320          *
4321          *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4322          *
4323          *   $provide.value('halfOf', function(value) {
4324          *     return value / 2;
4325          *   });
4326          * ```
4327          */
4328
4329
4330         /**
4331          * @ngdoc method
4332          * @name $provide#constant
4333          * @description
4334          *
4335          * Register a **constant service**, such as a string, a number, an array, an object or a function,
4336          * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4337          * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4338          * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4339          *
4340          * @param {string} name The name of the constant.
4341          * @param {*} value The constant value.
4342          * @returns {Object} registered instance
4343          *
4344          * @example
4345          * Here a some examples of creating constants:
4346          * ```js
4347          *   $provide.constant('SHARD_HEIGHT', 306);
4348          *
4349          *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4350          *
4351          *   $provide.constant('double', function(value) {
4352          *     return value * 2;
4353          *   });
4354          * ```
4355          */
4356
4357
4358         /**
4359          * @ngdoc method
4360          * @name $provide#decorator
4361          * @description
4362          *
4363          * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4364          * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4365          * service. The object returned by the decorator may be the original service, or a new service
4366          * object which replaces or wraps and delegates to the original service.
4367          *
4368          * @param {string} name The name of the service to decorate.
4369          * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4370          *    instantiated and should return the decorated service instance. The function is called using
4371          *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4372          *    Local injection arguments:
4373          *
4374          *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4375          *      decorated or delegated to.
4376          *
4377          * @example
4378          * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4379          * calls to {@link ng.$log#error $log.warn()}.
4380          * ```js
4381          *   $provide.decorator('$log', ['$delegate', function($delegate) {
4382          *     $delegate.warn = $delegate.error;
4383          *     return $delegate;
4384          *   }]);
4385          * ```
4386          */
4387
4388
4389         function createInjector(modulesToLoad, strictDi) {
4390           strictDi = (strictDi === true);
4391           var INSTANTIATING = {},
4392               providerSuffix = 'Provider',
4393               path = [],
4394               loadedModules = new HashMap([], true),
4395               providerCache = {
4396                 $provide: {
4397                     provider: supportObject(provider),
4398                     factory: supportObject(factory),
4399                     service: supportObject(service),
4400                     value: supportObject(value),
4401                     constant: supportObject(constant),
4402                     decorator: decorator
4403                   }
4404               },
4405               providerInjector = (providerCache.$injector =
4406                   createInternalInjector(providerCache, function(serviceName, caller) {
4407                     if (angular.isString(caller)) {
4408                       path.push(caller);
4409                     }
4410                     throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4411                   })),
4412               instanceCache = {},
4413               instanceInjector = (instanceCache.$injector =
4414                   createInternalInjector(instanceCache, function(serviceName, caller) {
4415                     var provider = providerInjector.get(serviceName + providerSuffix, caller);
4416                     return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4417                   }));
4418
4419
4420           forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4421
4422           return instanceInjector;
4423
4424           ////////////////////////////////////
4425           // $provider
4426           ////////////////////////////////////
4427
4428           function supportObject(delegate) {
4429             return function(key, value) {
4430               if (isObject(key)) {
4431                 forEach(key, reverseParams(delegate));
4432               } else {
4433                 return delegate(key, value);
4434               }
4435             };
4436           }
4437
4438           function provider(name, provider_) {
4439             assertNotHasOwnProperty(name, 'service');
4440             if (isFunction(provider_) || isArray(provider_)) {
4441               provider_ = providerInjector.instantiate(provider_);
4442             }
4443             if (!provider_.$get) {
4444               throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4445             }
4446             return providerCache[name + providerSuffix] = provider_;
4447           }
4448
4449           function enforceReturnValue(name, factory) {
4450             return function enforcedReturnValue() {
4451               var result = instanceInjector.invoke(factory, this);
4452               if (isUndefined(result)) {
4453                 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4454               }
4455               return result;
4456             };
4457           }
4458
4459           function factory(name, factoryFn, enforce) {
4460             return provider(name, {
4461               $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4462             });
4463           }
4464
4465           function service(name, constructor) {
4466             return factory(name, ['$injector', function($injector) {
4467               return $injector.instantiate(constructor);
4468             }]);
4469           }
4470
4471           function value(name, val) { return factory(name, valueFn(val), false); }
4472
4473           function constant(name, value) {
4474             assertNotHasOwnProperty(name, 'constant');
4475             providerCache[name] = value;
4476             instanceCache[name] = value;
4477           }
4478
4479           function decorator(serviceName, decorFn) {
4480             var origProvider = providerInjector.get(serviceName + providerSuffix),
4481                 orig$get = origProvider.$get;
4482
4483             origProvider.$get = function() {
4484               var origInstance = instanceInjector.invoke(orig$get, origProvider);
4485               return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4486             };
4487           }
4488
4489           ////////////////////////////////////
4490           // Module Loading
4491           ////////////////////////////////////
4492           function loadModules(modulesToLoad) {
4493             assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4494             var runBlocks = [], moduleFn;
4495             forEach(modulesToLoad, function(module) {
4496               if (loadedModules.get(module)) return;
4497               loadedModules.put(module, true);
4498
4499               function runInvokeQueue(queue) {
4500                 var i, ii;
4501                 for (i = 0, ii = queue.length; i < ii; i++) {
4502                   var invokeArgs = queue[i],
4503                       provider = providerInjector.get(invokeArgs[0]);
4504
4505                   provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4506                 }
4507               }
4508
4509               try {
4510                 if (isString(module)) {
4511                   moduleFn = angularModule(module);
4512                   runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4513                   runInvokeQueue(moduleFn._invokeQueue);
4514                   runInvokeQueue(moduleFn._configBlocks);
4515                 } else if (isFunction(module)) {
4516                     runBlocks.push(providerInjector.invoke(module));
4517                 } else if (isArray(module)) {
4518                     runBlocks.push(providerInjector.invoke(module));
4519                 } else {
4520                   assertArgFn(module, 'module');
4521                 }
4522               } catch (e) {
4523                 if (isArray(module)) {
4524                   module = module[module.length - 1];
4525                 }
4526                 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4527                   // Safari & FF's stack traces don't contain error.message content
4528                   // unlike those of Chrome and IE
4529                   // So if stack doesn't contain message, we create a new string that contains both.
4530                   // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4531                   /* jshint -W022 */
4532                   e = e.message + '\n' + e.stack;
4533                 }
4534                 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4535                           module, e.stack || e.message || e);
4536               }
4537             });
4538             return runBlocks;
4539           }
4540
4541           ////////////////////////////////////
4542           // internal Injector
4543           ////////////////////////////////////
4544
4545           function createInternalInjector(cache, factory) {
4546
4547             function getService(serviceName, caller) {
4548               if (cache.hasOwnProperty(serviceName)) {
4549                 if (cache[serviceName] === INSTANTIATING) {
4550                   throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4551                             serviceName + ' <- ' + path.join(' <- '));
4552                 }
4553                 return cache[serviceName];
4554               } else {
4555                 try {
4556                   path.unshift(serviceName);
4557                   cache[serviceName] = INSTANTIATING;
4558                   return cache[serviceName] = factory(serviceName, caller);
4559                 } catch (err) {
4560                   if (cache[serviceName] === INSTANTIATING) {
4561                     delete cache[serviceName];
4562                   }
4563                   throw err;
4564                 } finally {
4565                   path.shift();
4566                 }
4567               }
4568             }
4569
4570             function invoke(fn, self, locals, serviceName) {
4571               if (typeof locals === 'string') {
4572                 serviceName = locals;
4573                 locals = null;
4574               }
4575
4576               var args = [],
4577                   $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4578                   length, i,
4579                   key;
4580
4581               for (i = 0, length = $inject.length; i < length; i++) {
4582                 key = $inject[i];
4583                 if (typeof key !== 'string') {
4584                   throw $injectorMinErr('itkn',
4585                           'Incorrect injection token! Expected service name as string, got {0}', key);
4586                 }
4587                 args.push(
4588                   locals && locals.hasOwnProperty(key)
4589                   ? locals[key]
4590                   : getService(key, serviceName)
4591                 );
4592               }
4593               if (isArray(fn)) {
4594                 fn = fn[length];
4595               }
4596
4597               // http://jsperf.com/angularjs-invoke-apply-vs-switch
4598               // #5388
4599               return fn.apply(self, args);
4600             }
4601
4602             function instantiate(Type, locals, serviceName) {
4603               // Check if Type is annotated and use just the given function at n-1 as parameter
4604               // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4605               // Object creation: http://jsperf.com/create-constructor/2
4606               var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4607               var returnedValue = invoke(Type, instance, locals, serviceName);
4608
4609               return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4610             }
4611
4612             return {
4613               invoke: invoke,
4614               instantiate: instantiate,
4615               get: getService,
4616               annotate: createInjector.$$annotate,
4617               has: function(name) {
4618                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4619               }
4620             };
4621           }
4622         }
4623
4624         createInjector.$$annotate = annotate;
4625
4626         /**
4627          * @ngdoc provider
4628          * @name $anchorScrollProvider
4629          *
4630          * @description
4631          * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4632          * {@link ng.$location#hash $location.hash()} changes.
4633          */
4634         function $AnchorScrollProvider() {
4635
4636           var autoScrollingEnabled = true;
4637
4638           /**
4639            * @ngdoc method
4640            * @name $anchorScrollProvider#disableAutoScrolling
4641            *
4642            * @description
4643            * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4644            * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4645            * Use this method to disable automatic scrolling.
4646            *
4647            * If automatic scrolling is disabled, one must explicitly call
4648            * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4649            * current hash.
4650            */
4651           this.disableAutoScrolling = function() {
4652             autoScrollingEnabled = false;
4653           };
4654
4655           /**
4656            * @ngdoc service
4657            * @name $anchorScroll
4658            * @kind function
4659            * @requires $window
4660            * @requires $location
4661            * @requires $rootScope
4662            *
4663            * @description
4664            * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4665            * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4666            * in the
4667            * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4668            *
4669            * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4670            * match any anchor whenever it changes. This can be disabled by calling
4671            * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4672            *
4673            * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4674            * vertical scroll-offset (either fixed or dynamic).
4675            *
4676            * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4677            *                       {@link ng.$location#hash $location.hash()} will be used.
4678            *
4679            * @property {(number|function|jqLite)} yOffset
4680            * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4681            * positioned elements at the top of the page, such as navbars, headers etc.
4682            *
4683            * `yOffset` can be specified in various ways:
4684            * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4685            * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4686            *   a number representing the offset (in pixels).<br /><br />
4687            * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4688            *   the top of the page to the element's bottom will be used as offset.<br />
4689            *   **Note**: The element will be taken into account only as long as its `position` is set to
4690            *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4691            *   their height and/or positioning according to the viewport's size.
4692            *
4693            * <br />
4694            * <div class="alert alert-warning">
4695            * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4696            * not some child element.
4697            * </div>
4698            *
4699            * @example
4700              <example module="anchorScrollExample">
4701                <file name="index.html">
4702                  <div id="scrollArea" ng-controller="ScrollController">
4703                    <a ng-click="gotoBottom()">Go to bottom</a>
4704                    <a id="bottom"></a> You're at the bottom!
4705                  </div>
4706                </file>
4707                <file name="script.js">
4708                  angular.module('anchorScrollExample', [])
4709                    .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4710                      function ($scope, $location, $anchorScroll) {
4711                        $scope.gotoBottom = function() {
4712                          // set the location.hash to the id of
4713                          // the element you wish to scroll to.
4714                          $location.hash('bottom');
4715
4716                          // call $anchorScroll()
4717                          $anchorScroll();
4718                        };
4719                      }]);
4720                </file>
4721                <file name="style.css">
4722                  #scrollArea {
4723                    height: 280px;
4724                    overflow: auto;
4725                  }
4726
4727                  #bottom {
4728                    display: block;
4729                    margin-top: 2000px;
4730                  }
4731                </file>
4732              </example>
4733            *
4734            * <hr />
4735            * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4736            * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4737            *
4738            * @example
4739              <example module="anchorScrollOffsetExample">
4740                <file name="index.html">
4741                  <div class="fixed-header" ng-controller="headerCtrl">
4742                    <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4743                      Go to anchor {{x}}
4744                    </a>
4745                  </div>
4746                  <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4747                    Anchor {{x}} of 5
4748                  </div>
4749                </file>
4750                <file name="script.js">
4751                  angular.module('anchorScrollOffsetExample', [])
4752                    .run(['$anchorScroll', function($anchorScroll) {
4753                      $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4754                    }])
4755                    .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4756                      function ($anchorScroll, $location, $scope) {
4757                        $scope.gotoAnchor = function(x) {
4758                          var newHash = 'anchor' + x;
4759                          if ($location.hash() !== newHash) {
4760                            // set the $location.hash to `newHash` and
4761                            // $anchorScroll will automatically scroll to it
4762                            $location.hash('anchor' + x);
4763                          } else {
4764                            // call $anchorScroll() explicitly,
4765                            // since $location.hash hasn't changed
4766                            $anchorScroll();
4767                          }
4768                        };
4769                      }
4770                    ]);
4771                </file>
4772                <file name="style.css">
4773                  body {
4774                    padding-top: 50px;
4775                  }
4776
4777                  .anchor {
4778                    border: 2px dashed DarkOrchid;
4779                    padding: 10px 10px 200px 10px;
4780                  }
4781
4782                  .fixed-header {
4783                    background-color: rgba(0, 0, 0, 0.2);
4784                    height: 50px;
4785                    position: fixed;
4786                    top: 0; left: 0; right: 0;
4787                  }
4788
4789                  .fixed-header > a {
4790                    display: inline-block;
4791                    margin: 5px 15px;
4792                  }
4793                </file>
4794              </example>
4795            */
4796           this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4797             var document = $window.document;
4798
4799             // Helper function to get first anchor from a NodeList
4800             // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4801             //  and working in all supported browsers.)
4802             function getFirstAnchor(list) {
4803               var result = null;
4804               Array.prototype.some.call(list, function(element) {
4805                 if (nodeName_(element) === 'a') {
4806                   result = element;
4807                   return true;
4808                 }
4809               });
4810               return result;
4811             }
4812
4813             function getYOffset() {
4814
4815               var offset = scroll.yOffset;
4816
4817               if (isFunction(offset)) {
4818                 offset = offset();
4819               } else if (isElement(offset)) {
4820                 var elem = offset[0];
4821                 var style = $window.getComputedStyle(elem);
4822                 if (style.position !== 'fixed') {
4823                   offset = 0;
4824                 } else {
4825                   offset = elem.getBoundingClientRect().bottom;
4826                 }
4827               } else if (!isNumber(offset)) {
4828                 offset = 0;
4829               }
4830
4831               return offset;
4832             }
4833
4834             function scrollTo(elem) {
4835               if (elem) {
4836                 elem.scrollIntoView();
4837
4838                 var offset = getYOffset();
4839
4840                 if (offset) {
4841                   // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4842                   // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4843                   // top of the viewport.
4844                   //
4845                   // IF the number of pixels from the top of `elem` to the end of the page's content is less
4846                   // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4847                   // way down the page.
4848                   //
4849                   // This is often the case for elements near the bottom of the page.
4850                   //
4851                   // In such cases we do not need to scroll the whole `offset` up, just the difference between
4852                   // the top of the element and the offset, which is enough to align the top of `elem` at the
4853                   // desired position.
4854                   var elemTop = elem.getBoundingClientRect().top;
4855                   $window.scrollBy(0, elemTop - offset);
4856                 }
4857               } else {
4858                 $window.scrollTo(0, 0);
4859               }
4860             }
4861
4862             function scroll(hash) {
4863               hash = isString(hash) ? hash : $location.hash();
4864               var elm;
4865
4866               // empty hash, scroll to the top of the page
4867               if (!hash) scrollTo(null);
4868
4869               // element with given id
4870               else if ((elm = document.getElementById(hash))) scrollTo(elm);
4871
4872               // first anchor with given name :-D
4873               else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4874
4875               // no element and hash == 'top', scroll to the top of the page
4876               else if (hash === 'top') scrollTo(null);
4877             }
4878
4879             // does not scroll when user clicks on anchor link that is currently on
4880             // (no url change, no $location.hash() change), browser native does scroll
4881             if (autoScrollingEnabled) {
4882               $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4883                 function autoScrollWatchAction(newVal, oldVal) {
4884                   // skip the initial scroll if $location.hash is empty
4885                   if (newVal === oldVal && newVal === '') return;
4886
4887                   jqLiteDocumentLoaded(function() {
4888                     $rootScope.$evalAsync(scroll);
4889                   });
4890                 });
4891             }
4892
4893             return scroll;
4894           }];
4895         }
4896
4897         var $animateMinErr = minErr('$animate');
4898         var ELEMENT_NODE = 1;
4899         var NG_ANIMATE_CLASSNAME = 'ng-animate';
4900
4901         function mergeClasses(a,b) {
4902           if (!a && !b) return '';
4903           if (!a) return b;
4904           if (!b) return a;
4905           if (isArray(a)) a = a.join(' ');
4906           if (isArray(b)) b = b.join(' ');
4907           return a + ' ' + b;
4908         }
4909
4910         function extractElementNode(element) {
4911           for (var i = 0; i < element.length; i++) {
4912             var elm = element[i];
4913             if (elm.nodeType === ELEMENT_NODE) {
4914               return elm;
4915             }
4916           }
4917         }
4918
4919         function splitClasses(classes) {
4920           if (isString(classes)) {
4921             classes = classes.split(' ');
4922           }
4923
4924           // Use createMap() to prevent class assumptions involving property names in
4925           // Object.prototype
4926           var obj = createMap();
4927           forEach(classes, function(klass) {
4928             // sometimes the split leaves empty string values
4929             // incase extra spaces were applied to the options
4930             if (klass.length) {
4931               obj[klass] = true;
4932             }
4933           });
4934           return obj;
4935         }
4936
4937         // if any other type of options value besides an Object value is
4938         // passed into the $animate.method() animation then this helper code
4939         // will be run which will ignore it. While this patch is not the
4940         // greatest solution to this, a lot of existing plugins depend on
4941         // $animate to either call the callback (< 1.2) or return a promise
4942         // that can be changed. This helper function ensures that the options
4943         // are wiped clean incase a callback function is provided.
4944         function prepareAnimateOptions(options) {
4945           return isObject(options)
4946               ? options
4947               : {};
4948         }
4949
4950         var $$CoreAnimateRunnerProvider = function() {
4951           this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4952             function AnimateRunner() {}
4953             AnimateRunner.all = noop;
4954             AnimateRunner.chain = noop;
4955             AnimateRunner.prototype = {
4956               end: noop,
4957               cancel: noop,
4958               resume: noop,
4959               pause: noop,
4960               complete: noop,
4961               then: function(pass, fail) {
4962                 return $q(function(resolve) {
4963                   $$rAF(function() {
4964                     resolve();
4965                   });
4966                 }).then(pass, fail);
4967               }
4968             };
4969             return AnimateRunner;
4970           }];
4971         };
4972
4973         // this is prefixed with Core since it conflicts with
4974         // the animateQueueProvider defined in ngAnimate/animateQueue.js
4975         var $$CoreAnimateQueueProvider = function() {
4976           var postDigestQueue = new HashMap();
4977           var postDigestElements = [];
4978
4979           this.$get = ['$$AnimateRunner', '$rootScope',
4980                function($$AnimateRunner,   $rootScope) {
4981             return {
4982               enabled: noop,
4983               on: noop,
4984               off: noop,
4985               pin: noop,
4986
4987               push: function(element, event, options, domOperation) {
4988                 domOperation        && domOperation();
4989
4990                 options = options || {};
4991                 options.from        && element.css(options.from);
4992                 options.to          && element.css(options.to);
4993
4994                 if (options.addClass || options.removeClass) {
4995                   addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4996                 }
4997
4998                 return new $$AnimateRunner(); // jshint ignore:line
4999               }
5000             };
5001
5002
5003             function updateData(data, classes, value) {
5004               var changed = false;
5005               if (classes) {
5006                 classes = isString(classes) ? classes.split(' ') :
5007                           isArray(classes) ? classes : [];
5008                 forEach(classes, function(className) {
5009                   if (className) {
5010                     changed = true;
5011                     data[className] = value;
5012                   }
5013                 });
5014               }
5015               return changed;
5016             }
5017
5018             function handleCSSClassChanges() {
5019               forEach(postDigestElements, function(element) {
5020                 var data = postDigestQueue.get(element);
5021                 if (data) {
5022                   var existing = splitClasses(element.attr('class'));
5023                   var toAdd = '';
5024                   var toRemove = '';
5025                   forEach(data, function(status, className) {
5026                     var hasClass = !!existing[className];
5027                     if (status !== hasClass) {
5028                       if (status) {
5029                         toAdd += (toAdd.length ? ' ' : '') + className;
5030                       } else {
5031                         toRemove += (toRemove.length ? ' ' : '') + className;
5032                       }
5033                     }
5034                   });
5035
5036                   forEach(element, function(elm) {
5037                     toAdd    && jqLiteAddClass(elm, toAdd);
5038                     toRemove && jqLiteRemoveClass(elm, toRemove);
5039                   });
5040                   postDigestQueue.remove(element);
5041                 }
5042               });
5043               postDigestElements.length = 0;
5044             }
5045
5046
5047             function addRemoveClassesPostDigest(element, add, remove) {
5048               var data = postDigestQueue.get(element) || {};
5049
5050               var classesAdded = updateData(data, add, true);
5051               var classesRemoved = updateData(data, remove, false);
5052
5053               if (classesAdded || classesRemoved) {
5054
5055                 postDigestQueue.put(element, data);
5056                 postDigestElements.push(element);
5057
5058                 if (postDigestElements.length === 1) {
5059                   $rootScope.$$postDigest(handleCSSClassChanges);
5060                 }
5061               }
5062             }
5063           }];
5064         };
5065
5066         /**
5067          * @ngdoc provider
5068          * @name $animateProvider
5069          *
5070          * @description
5071          * Default implementation of $animate that doesn't perform any animations, instead just
5072          * synchronously performs DOM updates and resolves the returned runner promise.
5073          *
5074          * In order to enable animations the `ngAnimate` module has to be loaded.
5075          *
5076          * To see the functional implementation check out `src/ngAnimate/animate.js`.
5077          */
5078         var $AnimateProvider = ['$provide', function($provide) {
5079           var provider = this;
5080
5081           this.$$registeredAnimations = Object.create(null);
5082
5083            /**
5084            * @ngdoc method
5085            * @name $animateProvider#register
5086            *
5087            * @description
5088            * Registers a new injectable animation factory function. The factory function produces the
5089            * animation object which contains callback functions for each event that is expected to be
5090            * animated.
5091            *
5092            *   * `eventFn`: `function(element, ... , doneFunction, options)`
5093            *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5094            *   on the type of animation additional arguments will be injected into the animation function. The
5095            *   list below explains the function signatures for the different animation methods:
5096            *
5097            *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5098            *   - addClass: function(element, addedClasses, doneFunction, options)
5099            *   - removeClass: function(element, removedClasses, doneFunction, options)
5100            *   - enter, leave, move: function(element, doneFunction, options)
5101            *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5102            *
5103            *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5104            *
5105            * ```js
5106            *   return {
5107            *     //enter, leave, move signature
5108            *     eventFn : function(element, done, options) {
5109            *       //code to run the animation
5110            *       //once complete, then run done()
5111            *       return function endFunction(wasCancelled) {
5112            *         //code to cancel the animation
5113            *       }
5114            *     }
5115            *   }
5116            * ```
5117            *
5118            * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5119            * @param {Function} factory The factory function that will be executed to return the animation
5120            *                           object.
5121            */
5122           this.register = function(name, factory) {
5123             if (name && name.charAt(0) !== '.') {
5124               throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5125             }
5126
5127             var key = name + '-animation';
5128             provider.$$registeredAnimations[name.substr(1)] = key;
5129             $provide.factory(key, factory);
5130           };
5131
5132           /**
5133            * @ngdoc method
5134            * @name $animateProvider#classNameFilter
5135            *
5136            * @description
5137            * Sets and/or returns the CSS class regular expression that is checked when performing
5138            * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5139            * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5140            * When setting the `classNameFilter` value, animations will only be performed on elements
5141            * that successfully match the filter expression. This in turn can boost performance
5142            * for low-powered devices as well as applications containing a lot of structural operations.
5143            * @param {RegExp=} expression The className expression which will be checked against all animations
5144            * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5145            */
5146           this.classNameFilter = function(expression) {
5147             if (arguments.length === 1) {
5148               this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5149               if (this.$$classNameFilter) {
5150                 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5151                 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5152                   throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5153
5154                 }
5155               }
5156             }
5157             return this.$$classNameFilter;
5158           };
5159
5160           this.$get = ['$$animateQueue', function($$animateQueue) {
5161             function domInsert(element, parentElement, afterElement) {
5162               // if for some reason the previous element was removed
5163               // from the dom sometime before this code runs then let's
5164               // just stick to using the parent element as the anchor
5165               if (afterElement) {
5166                 var afterNode = extractElementNode(afterElement);
5167                 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5168                   afterElement = null;
5169                 }
5170               }
5171               afterElement ? afterElement.after(element) : parentElement.prepend(element);
5172             }
5173
5174             /**
5175              * @ngdoc service
5176              * @name $animate
5177              * @description The $animate service exposes a series of DOM utility methods that provide support
5178              * for animation hooks. The default behavior is the application of DOM operations, however,
5179              * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5180              * to ensure that animation runs with the triggered DOM operation.
5181              *
5182              * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5183              * included and only when it is active then the animation hooks that `$animate` triggers will be
5184              * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5185              * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5186              * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5187              *
5188              * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5189              *
5190              * To learn more about enabling animation support, click here to visit the
5191              * {@link ngAnimate ngAnimate module page}.
5192              */
5193             return {
5194               // we don't call it directly since non-existant arguments may
5195               // be interpreted as null within the sub enabled function
5196
5197               /**
5198                *
5199                * @ngdoc method
5200                * @name $animate#on
5201                * @kind function
5202                * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5203                *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5204                *    is fired with the following params:
5205                *
5206                * ```js
5207                * $animate.on('enter', container,
5208                *    function callback(element, phase) {
5209                *      // cool we detected an enter animation within the container
5210                *    }
5211                * );
5212                * ```
5213                *
5214                * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5215                * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5216                *     as well as among its children
5217                * @param {Function} callback the callback function that will be fired when the listener is triggered
5218                *
5219                * The arguments present in the callback function are:
5220                * * `element` - The captured DOM element that the animation was fired on.
5221                * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5222                */
5223               on: $$animateQueue.on,
5224
5225               /**
5226                *
5227                * @ngdoc method
5228                * @name $animate#off
5229                * @kind function
5230                * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5231                * can be used in three different ways depending on the arguments:
5232                *
5233                * ```js
5234                * // remove all the animation event listeners listening for `enter`
5235                * $animate.off('enter');
5236                *
5237                * // remove all the animation event listeners listening for `enter` on the given element and its children
5238                * $animate.off('enter', container);
5239                *
5240                * // remove the event listener function provided by `listenerFn` that is set
5241                * // to listen for `enter` on the given `element` as well as its children
5242                * $animate.off('enter', container, callback);
5243                * ```
5244                *
5245                * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5246                * @param {DOMElement=} container the container element the event listener was placed on
5247                * @param {Function=} callback the callback function that was registered as the listener
5248                */
5249               off: $$animateQueue.off,
5250
5251               /**
5252                * @ngdoc method
5253                * @name $animate#pin
5254                * @kind function
5255                * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5256                *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5257                *    element despite being outside the realm of the application or within another application. Say for example if the application
5258                *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5259                *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5260                *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5261                *
5262                *    Note that this feature is only active when the `ngAnimate` module is used.
5263                *
5264                * @param {DOMElement} element the external element that will be pinned
5265                * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5266                */
5267               pin: $$animateQueue.pin,
5268
5269               /**
5270                *
5271                * @ngdoc method
5272                * @name $animate#enabled
5273                * @kind function
5274                * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5275                * function can be called in four ways:
5276                *
5277                * ```js
5278                * // returns true or false
5279                * $animate.enabled();
5280                *
5281                * // changes the enabled state for all animations
5282                * $animate.enabled(false);
5283                * $animate.enabled(true);
5284                *
5285                * // returns true or false if animations are enabled for an element
5286                * $animate.enabled(element);
5287                *
5288                * // changes the enabled state for an element and its children
5289                * $animate.enabled(element, true);
5290                * $animate.enabled(element, false);
5291                * ```
5292                *
5293                * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5294                * @param {boolean=} enabled whether or not the animations will be enabled for the element
5295                *
5296                * @return {boolean} whether or not animations are enabled
5297                */
5298               enabled: $$animateQueue.enabled,
5299
5300               /**
5301                * @ngdoc method
5302                * @name $animate#cancel
5303                * @kind function
5304                * @description Cancels the provided animation.
5305                *
5306                * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5307                */
5308               cancel: function(runner) {
5309                 runner.end && runner.end();
5310               },
5311
5312               /**
5313                *
5314                * @ngdoc method
5315                * @name $animate#enter
5316                * @kind function
5317                * @description Inserts the element into the DOM either after the `after` element (if provided) or
5318                *   as the first child within the `parent` element and then triggers an animation.
5319                *   A promise is returned that will be resolved during the next digest once the animation
5320                *   has completed.
5321                *
5322                * @param {DOMElement} element the element which will be inserted into the DOM
5323                * @param {DOMElement} parent the parent element which will append the element as
5324                *   a child (so long as the after element is not present)
5325                * @param {DOMElement=} after the sibling element after which the element will be appended
5326                * @param {object=} options an optional collection of options/styles that will be applied to the element
5327                *
5328                * @return {Promise} the animation callback promise
5329                */
5330               enter: function(element, parent, after, options) {
5331                 parent = parent && jqLite(parent);
5332                 after = after && jqLite(after);
5333                 parent = parent || after.parent();
5334                 domInsert(element, parent, after);
5335                 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5336               },
5337
5338               /**
5339                *
5340                * @ngdoc method
5341                * @name $animate#move
5342                * @kind function
5343                * @description Inserts (moves) the element into its new position in the DOM either after
5344                *   the `after` element (if provided) or as the first child within the `parent` element
5345                *   and then triggers an animation. A promise is returned that will be resolved
5346                *   during the next digest once the animation has completed.
5347                *
5348                * @param {DOMElement} element the element which will be moved into the new DOM position
5349                * @param {DOMElement} parent the parent element which will append the element as
5350                *   a child (so long as the after element is not present)
5351                * @param {DOMElement=} after the sibling element after which the element will be appended
5352                * @param {object=} options an optional collection of options/styles that will be applied to the element
5353                *
5354                * @return {Promise} the animation callback promise
5355                */
5356               move: function(element, parent, after, options) {
5357                 parent = parent && jqLite(parent);
5358                 after = after && jqLite(after);
5359                 parent = parent || after.parent();
5360                 domInsert(element, parent, after);
5361                 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5362               },
5363
5364               /**
5365                * @ngdoc method
5366                * @name $animate#leave
5367                * @kind function
5368                * @description Triggers an animation and then removes the element from the DOM.
5369                * When the function is called a promise is returned that will be resolved during the next
5370                * digest once the animation has completed.
5371                *
5372                * @param {DOMElement} element the element which will be removed from the DOM
5373                * @param {object=} options an optional collection of options/styles that will be applied to the element
5374                *
5375                * @return {Promise} the animation callback promise
5376                */
5377               leave: function(element, options) {
5378                 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5379                   element.remove();
5380                 });
5381               },
5382
5383               /**
5384                * @ngdoc method
5385                * @name $animate#addClass
5386                * @kind function
5387                *
5388                * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5389                *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5390                *   animation if element already contains the CSS class or if the class is removed at a later step.
5391                *   Note that class-based animations are treated differently compared to structural animations
5392                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5393                *   depending if CSS or JavaScript animations are used.
5394                *
5395                * @param {DOMElement} element the element which the CSS classes will be applied to
5396                * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5397                * @param {object=} options an optional collection of options/styles that will be applied to the element
5398                *
5399                * @return {Promise} the animation callback promise
5400                */
5401               addClass: function(element, className, options) {
5402                 options = prepareAnimateOptions(options);
5403                 options.addClass = mergeClasses(options.addclass, className);
5404                 return $$animateQueue.push(element, 'addClass', options);
5405               },
5406
5407               /**
5408                * @ngdoc method
5409                * @name $animate#removeClass
5410                * @kind function
5411                *
5412                * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5413                *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5414                *   animation if element does not contain the CSS class or if the class is added at a later step.
5415                *   Note that class-based animations are treated differently compared to structural animations
5416                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5417                *   depending if CSS or JavaScript animations are used.
5418                *
5419                * @param {DOMElement} element the element which the CSS classes will be applied to
5420                * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5421                * @param {object=} options an optional collection of options/styles that will be applied to the element
5422                *
5423                * @return {Promise} the animation callback promise
5424                */
5425               removeClass: function(element, className, options) {
5426                 options = prepareAnimateOptions(options);
5427                 options.removeClass = mergeClasses(options.removeClass, className);
5428                 return $$animateQueue.push(element, 'removeClass', options);
5429               },
5430
5431               /**
5432                * @ngdoc method
5433                * @name $animate#setClass
5434                * @kind function
5435                *
5436                * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5437                *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5438                *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5439                *    passed. Note that class-based animations are treated differently compared to structural animations
5440                *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5441                *    depending if CSS or JavaScript animations are used.
5442                *
5443                * @param {DOMElement} element the element which the CSS classes will be applied to
5444                * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5445                * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5446                * @param {object=} options an optional collection of options/styles that will be applied to the element
5447                *
5448                * @return {Promise} the animation callback promise
5449                */
5450               setClass: function(element, add, remove, options) {
5451                 options = prepareAnimateOptions(options);
5452                 options.addClass = mergeClasses(options.addClass, add);
5453                 options.removeClass = mergeClasses(options.removeClass, remove);
5454                 return $$animateQueue.push(element, 'setClass', options);
5455               },
5456
5457               /**
5458                * @ngdoc method
5459                * @name $animate#animate
5460                * @kind function
5461                *
5462                * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5463                * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5464                * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5465                * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5466                * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5467                *
5468                * @param {DOMElement} element the element which the CSS styles will be applied to
5469                * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5470                * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5471                * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5472                *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5473                *    (Note that if no animation is detected then this value will not be appplied to the element.)
5474                * @param {object=} options an optional collection of options/styles that will be applied to the element
5475                *
5476                * @return {Promise} the animation callback promise
5477                */
5478               animate: function(element, from, to, className, options) {
5479                 options = prepareAnimateOptions(options);
5480                 options.from = options.from ? extend(options.from, from) : from;
5481                 options.to   = options.to   ? extend(options.to, to)     : to;
5482
5483                 className = className || 'ng-inline-animate';
5484                 options.tempClasses = mergeClasses(options.tempClasses, className);
5485                 return $$animateQueue.push(element, 'animate', options);
5486               }
5487             };
5488           }];
5489         }];
5490
5491         /**
5492          * @ngdoc service
5493          * @name $animateCss
5494          * @kind object
5495          *
5496          * @description
5497          * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5498          * then the `$animateCss` service will actually perform animations.
5499          *
5500          * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5501          */
5502         var $CoreAnimateCssProvider = function() {
5503           this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5504
5505             var RAFPromise = function() {};
5506             RAFPromise.prototype = {
5507               done: function(cancel) {
5508                 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5509               },
5510               end: function() {
5511                 this.done();
5512               },
5513               cancel: function() {
5514                 this.done(true);
5515               },
5516               getPromise: function() {
5517                 if (!this.defer) {
5518                   this.defer = $q.defer();
5519                 }
5520                 return this.defer.promise;
5521               },
5522               then: function(f1,f2) {
5523                 return this.getPromise().then(f1,f2);
5524               },
5525               'catch': function(f1) {
5526                 return this.getPromise()['catch'](f1);
5527               },
5528               'finally': function(f1) {
5529                 return this.getPromise()['finally'](f1);
5530               }
5531             };
5532
5533             return function(element, options) {
5534               // there is no point in applying the styles since
5535               // there is no animation that goes on at all in
5536               // this version of $animateCss.
5537               if (options.cleanupStyles) {
5538                 options.from = options.to = null;
5539               }
5540
5541               if (options.from) {
5542                 element.css(options.from);
5543                 options.from = null;
5544               }
5545
5546               var closed, runner = new RAFPromise();
5547               return {
5548                 start: run,
5549                 end: run
5550               };
5551
5552               function run() {
5553                 $$rAF(function() {
5554                   close();
5555                   if (!closed) {
5556                     runner.done();
5557                   }
5558                   closed = true;
5559                 });
5560                 return runner;
5561               }
5562
5563               function close() {
5564                 if (options.addClass) {
5565                   element.addClass(options.addClass);
5566                   options.addClass = null;
5567                 }
5568                 if (options.removeClass) {
5569                   element.removeClass(options.removeClass);
5570                   options.removeClass = null;
5571                 }
5572                 if (options.to) {
5573                   element.css(options.to);
5574                   options.to = null;
5575                 }
5576               }
5577             };
5578           }];
5579         };
5580
5581         /* global stripHash: true */
5582
5583         /**
5584          * ! This is a private undocumented service !
5585          *
5586          * @name $browser
5587          * @requires $log
5588          * @description
5589          * This object has two goals:
5590          *
5591          * - hide all the global state in the browser caused by the window object
5592          * - abstract away all the browser specific features and inconsistencies
5593          *
5594          * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5595          * service, which can be used for convenient testing of the application without the interaction with
5596          * the real browser apis.
5597          */
5598         /**
5599          * @param {object} window The global window object.
5600          * @param {object} document jQuery wrapped document.
5601          * @param {object} $log window.console or an object with the same interface.
5602          * @param {object} $sniffer $sniffer service
5603          */
5604         function Browser(window, document, $log, $sniffer) {
5605           var self = this,
5606               rawDocument = document[0],
5607               location = window.location,
5608               history = window.history,
5609               setTimeout = window.setTimeout,
5610               clearTimeout = window.clearTimeout,
5611               pendingDeferIds = {};
5612
5613           self.isMock = false;
5614
5615           var outstandingRequestCount = 0;
5616           var outstandingRequestCallbacks = [];
5617
5618           // TODO(vojta): remove this temporary api
5619           self.$$completeOutstandingRequest = completeOutstandingRequest;
5620           self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5621
5622           /**
5623            * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5624            * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5625            */
5626           function completeOutstandingRequest(fn) {
5627             try {
5628               fn.apply(null, sliceArgs(arguments, 1));
5629             } finally {
5630               outstandingRequestCount--;
5631               if (outstandingRequestCount === 0) {
5632                 while (outstandingRequestCallbacks.length) {
5633                   try {
5634                     outstandingRequestCallbacks.pop()();
5635                   } catch (e) {
5636                     $log.error(e);
5637                   }
5638                 }
5639               }
5640             }
5641           }
5642
5643           function getHash(url) {
5644             var index = url.indexOf('#');
5645             return index === -1 ? '' : url.substr(index);
5646           }
5647
5648           /**
5649            * @private
5650            * Note: this method is used only by scenario runner
5651            * TODO(vojta): prefix this method with $$ ?
5652            * @param {function()} callback Function that will be called when no outstanding request
5653            */
5654           self.notifyWhenNoOutstandingRequests = function(callback) {
5655             if (outstandingRequestCount === 0) {
5656               callback();
5657             } else {
5658               outstandingRequestCallbacks.push(callback);
5659             }
5660           };
5661
5662           //////////////////////////////////////////////////////////////
5663           // URL API
5664           //////////////////////////////////////////////////////////////
5665
5666           var cachedState, lastHistoryState,
5667               lastBrowserUrl = location.href,
5668               baseElement = document.find('base'),
5669               pendingLocation = null;
5670
5671           cacheState();
5672           lastHistoryState = cachedState;
5673
5674           /**
5675            * @name $browser#url
5676            *
5677            * @description
5678            * GETTER:
5679            * Without any argument, this method just returns current value of location.href.
5680            *
5681            * SETTER:
5682            * With at least one argument, this method sets url to new value.
5683            * If html5 history api supported, pushState/replaceState is used, otherwise
5684            * location.href/location.replace is used.
5685            * Returns its own instance to allow chaining
5686            *
5687            * NOTE: this api is intended for use only by the $location service. Please use the
5688            * {@link ng.$location $location service} to change url.
5689            *
5690            * @param {string} url New url (when used as setter)
5691            * @param {boolean=} replace Should new url replace current history record?
5692            * @param {object=} state object to use with pushState/replaceState
5693            */
5694           self.url = function(url, replace, state) {
5695             // In modern browsers `history.state` is `null` by default; treating it separately
5696             // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5697             // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5698             if (isUndefined(state)) {
5699               state = null;
5700             }
5701
5702             // Android Browser BFCache causes location, history reference to become stale.
5703             if (location !== window.location) location = window.location;
5704             if (history !== window.history) history = window.history;
5705
5706             // setter
5707             if (url) {
5708               var sameState = lastHistoryState === state;
5709
5710               // Don't change anything if previous and current URLs and states match. This also prevents
5711               // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5712               // See https://github.com/angular/angular.js/commit/ffb2701
5713               if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5714                 return self;
5715               }
5716               var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5717               lastBrowserUrl = url;
5718               lastHistoryState = state;
5719               // Don't use history API if only the hash changed
5720               // due to a bug in IE10/IE11 which leads
5721               // to not firing a `hashchange` nor `popstate` event
5722               // in some cases (see #9143).
5723               if ($sniffer.history && (!sameBase || !sameState)) {
5724                 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5725                 cacheState();
5726                 // Do the assignment again so that those two variables are referentially identical.
5727                 lastHistoryState = cachedState;
5728               } else {
5729                 if (!sameBase || pendingLocation) {
5730                   pendingLocation = url;
5731                 }
5732                 if (replace) {
5733                   location.replace(url);
5734                 } else if (!sameBase) {
5735                   location.href = url;
5736                 } else {
5737                   location.hash = getHash(url);
5738                 }
5739                 if (location.href !== url) {
5740                   pendingLocation = url;
5741                 }
5742               }
5743               return self;
5744             // getter
5745             } else {
5746               // - pendingLocation is needed as browsers don't allow to read out
5747               //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5748               //   https://openradar.appspot.com/22186109).
5749               // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5750               return pendingLocation || location.href.replace(/%27/g,"'");
5751             }
5752           };
5753
5754           /**
5755            * @name $browser#state
5756            *
5757            * @description
5758            * This method is a getter.
5759            *
5760            * Return history.state or null if history.state is undefined.
5761            *
5762            * @returns {object} state
5763            */
5764           self.state = function() {
5765             return cachedState;
5766           };
5767
5768           var urlChangeListeners = [],
5769               urlChangeInit = false;
5770
5771           function cacheStateAndFireUrlChange() {
5772             pendingLocation = null;
5773             cacheState();
5774             fireUrlChange();
5775           }
5776
5777           function getCurrentState() {
5778             try {
5779               return history.state;
5780             } catch (e) {
5781               // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5782             }
5783           }
5784
5785           // This variable should be used *only* inside the cacheState function.
5786           var lastCachedState = null;
5787           function cacheState() {
5788             // This should be the only place in $browser where `history.state` is read.
5789             cachedState = getCurrentState();
5790             cachedState = isUndefined(cachedState) ? null : cachedState;
5791
5792             // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5793             if (equals(cachedState, lastCachedState)) {
5794               cachedState = lastCachedState;
5795             }
5796             lastCachedState = cachedState;
5797           }
5798
5799           function fireUrlChange() {
5800             if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5801               return;
5802             }
5803
5804             lastBrowserUrl = self.url();
5805             lastHistoryState = cachedState;
5806             forEach(urlChangeListeners, function(listener) {
5807               listener(self.url(), cachedState);
5808             });
5809           }
5810
5811           /**
5812            * @name $browser#onUrlChange
5813            *
5814            * @description
5815            * Register callback function that will be called, when url changes.
5816            *
5817            * It's only called when the url is changed from outside of angular:
5818            * - user types different url into address bar
5819            * - user clicks on history (forward/back) button
5820            * - user clicks on a link
5821            *
5822            * It's not called when url is changed by $browser.url() method
5823            *
5824            * The listener gets called with new url as parameter.
5825            *
5826            * NOTE: this api is intended for use only by the $location service. Please use the
5827            * {@link ng.$location $location service} to monitor url changes in angular apps.
5828            *
5829            * @param {function(string)} listener Listener function to be called when url changes.
5830            * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5831            */
5832           self.onUrlChange = function(callback) {
5833             // TODO(vojta): refactor to use node's syntax for events
5834             if (!urlChangeInit) {
5835               // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5836               // don't fire popstate when user change the address bar and don't fire hashchange when url
5837               // changed by push/replaceState
5838
5839               // html5 history api - popstate event
5840               if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5841               // hashchange event
5842               jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5843
5844               urlChangeInit = true;
5845             }
5846
5847             urlChangeListeners.push(callback);
5848             return callback;
5849           };
5850
5851           /**
5852            * @private
5853            * Remove popstate and hashchange handler from window.
5854            *
5855            * NOTE: this api is intended for use only by $rootScope.
5856            */
5857           self.$$applicationDestroyed = function() {
5858             jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5859           };
5860
5861           /**
5862            * Checks whether the url has changed outside of Angular.
5863            * Needs to be exported to be able to check for changes that have been done in sync,
5864            * as hashchange/popstate events fire in async.
5865            */
5866           self.$$checkUrlChange = fireUrlChange;
5867
5868           //////////////////////////////////////////////////////////////
5869           // Misc API
5870           //////////////////////////////////////////////////////////////
5871
5872           /**
5873            * @name $browser#baseHref
5874            *
5875            * @description
5876            * Returns current <base href>
5877            * (always relative - without domain)
5878            *
5879            * @returns {string} The current base href
5880            */
5881           self.baseHref = function() {
5882             var href = baseElement.attr('href');
5883             return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5884           };
5885
5886           /**
5887            * @name $browser#defer
5888            * @param {function()} fn A function, who's execution should be deferred.
5889            * @param {number=} [delay=0] of milliseconds to defer the function execution.
5890            * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5891            *
5892            * @description
5893            * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5894            *
5895            * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5896            * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5897            * via `$browser.defer.flush()`.
5898            *
5899            */
5900           self.defer = function(fn, delay) {
5901             var timeoutId;
5902             outstandingRequestCount++;
5903             timeoutId = setTimeout(function() {
5904               delete pendingDeferIds[timeoutId];
5905               completeOutstandingRequest(fn);
5906             }, delay || 0);
5907             pendingDeferIds[timeoutId] = true;
5908             return timeoutId;
5909           };
5910
5911
5912           /**
5913            * @name $browser#defer.cancel
5914            *
5915            * @description
5916            * Cancels a deferred task identified with `deferId`.
5917            *
5918            * @param {*} deferId Token returned by the `$browser.defer` function.
5919            * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5920            *                    canceled.
5921            */
5922           self.defer.cancel = function(deferId) {
5923             if (pendingDeferIds[deferId]) {
5924               delete pendingDeferIds[deferId];
5925               clearTimeout(deferId);
5926               completeOutstandingRequest(noop);
5927               return true;
5928             }
5929             return false;
5930           };
5931
5932         }
5933
5934         function $BrowserProvider() {
5935           this.$get = ['$window', '$log', '$sniffer', '$document',
5936               function($window, $log, $sniffer, $document) {
5937                 return new Browser($window, $document, $log, $sniffer);
5938               }];
5939         }
5940
5941         /**
5942          * @ngdoc service
5943          * @name $cacheFactory
5944          *
5945          * @description
5946          * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5947          * them.
5948          *
5949          * ```js
5950          *
5951          *  var cache = $cacheFactory('cacheId');
5952          *  expect($cacheFactory.get('cacheId')).toBe(cache);
5953          *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5954          *
5955          *  cache.put("key", "value");
5956          *  cache.put("another key", "another value");
5957          *
5958          *  // We've specified no options on creation
5959          *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5960          *
5961          * ```
5962          *
5963          *
5964          * @param {string} cacheId Name or id of the newly created cache.
5965          * @param {object=} options Options object that specifies the cache behavior. Properties:
5966          *
5967          *   - `{number=}` `capacity` — turns the cache into LRU cache.
5968          *
5969          * @returns {object} Newly created cache object with the following set of methods:
5970          *
5971          * - `{object}` `info()` — Returns id, size, and options of cache.
5972          * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5973          *   it.
5974          * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5975          * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5976          * - `{void}` `removeAll()` — Removes all cached values.
5977          * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5978          *
5979          * @example
5980            <example module="cacheExampleApp">
5981              <file name="index.html">
5982                <div ng-controller="CacheController">
5983                  <input ng-model="newCacheKey" placeholder="Key">
5984                  <input ng-model="newCacheValue" placeholder="Value">
5985                  <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5986
5987                  <p ng-if="keys.length">Cached Values</p>
5988                  <div ng-repeat="key in keys">
5989                    <span ng-bind="key"></span>
5990                    <span>: </span>
5991                    <b ng-bind="cache.get(key)"></b>
5992                  </div>
5993
5994                  <p>Cache Info</p>
5995                  <div ng-repeat="(key, value) in cache.info()">
5996                    <span ng-bind="key"></span>
5997                    <span>: </span>
5998                    <b ng-bind="value"></b>
5999                  </div>
6000                </div>
6001              </file>
6002              <file name="script.js">
6003                angular.module('cacheExampleApp', []).
6004                  controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6005                    $scope.keys = [];
6006                    $scope.cache = $cacheFactory('cacheId');
6007                    $scope.put = function(key, value) {
6008                      if (angular.isUndefined($scope.cache.get(key))) {
6009                        $scope.keys.push(key);
6010                      }
6011                      $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6012                    };
6013                  }]);
6014              </file>
6015              <file name="style.css">
6016                p {
6017                  margin: 10px 0 3px;
6018                }
6019              </file>
6020            </example>
6021          */
6022         function $CacheFactoryProvider() {
6023
6024           this.$get = function() {
6025             var caches = {};
6026
6027             function cacheFactory(cacheId, options) {
6028               if (cacheId in caches) {
6029                 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6030               }
6031
6032               var size = 0,
6033                   stats = extend({}, options, {id: cacheId}),
6034                   data = createMap(),
6035                   capacity = (options && options.capacity) || Number.MAX_VALUE,
6036                   lruHash = createMap(),
6037                   freshEnd = null,
6038                   staleEnd = null;
6039
6040               /**
6041                * @ngdoc type
6042                * @name $cacheFactory.Cache
6043                *
6044                * @description
6045                * A cache object used to store and retrieve data, primarily used by
6046                * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6047                * templates and other data.
6048                *
6049                * ```js
6050                *  angular.module('superCache')
6051                *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6052                *      return $cacheFactory('super-cache');
6053                *    }]);
6054                * ```
6055                *
6056                * Example test:
6057                *
6058                * ```js
6059                *  it('should behave like a cache', inject(function(superCache) {
6060                *    superCache.put('key', 'value');
6061                *    superCache.put('another key', 'another value');
6062                *
6063                *    expect(superCache.info()).toEqual({
6064                *      id: 'super-cache',
6065                *      size: 2
6066                *    });
6067                *
6068                *    superCache.remove('another key');
6069                *    expect(superCache.get('another key')).toBeUndefined();
6070                *
6071                *    superCache.removeAll();
6072                *    expect(superCache.info()).toEqual({
6073                *      id: 'super-cache',
6074                *      size: 0
6075                *    });
6076                *  }));
6077                * ```
6078                */
6079               return caches[cacheId] = {
6080
6081                 /**
6082                  * @ngdoc method
6083                  * @name $cacheFactory.Cache#put
6084                  * @kind function
6085                  *
6086                  * @description
6087                  * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6088                  * retrieved later, and incrementing the size of the cache if the key was not already
6089                  * present in the cache. If behaving like an LRU cache, it will also remove stale
6090                  * entries from the set.
6091                  *
6092                  * It will not insert undefined values into the cache.
6093                  *
6094                  * @param {string} key the key under which the cached data is stored.
6095                  * @param {*} value the value to store alongside the key. If it is undefined, the key
6096                  *    will not be stored.
6097                  * @returns {*} the value stored.
6098                  */
6099                 put: function(key, value) {
6100                   if (isUndefined(value)) return;
6101                   if (capacity < Number.MAX_VALUE) {
6102                     var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6103
6104                     refresh(lruEntry);
6105                   }
6106
6107                   if (!(key in data)) size++;
6108                   data[key] = value;
6109
6110                   if (size > capacity) {
6111                     this.remove(staleEnd.key);
6112                   }
6113
6114                   return value;
6115                 },
6116
6117                 /**
6118                  * @ngdoc method
6119                  * @name $cacheFactory.Cache#get
6120                  * @kind function
6121                  *
6122                  * @description
6123                  * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6124                  *
6125                  * @param {string} key the key of the data to be retrieved
6126                  * @returns {*} the value stored.
6127                  */
6128                 get: function(key) {
6129                   if (capacity < Number.MAX_VALUE) {
6130                     var lruEntry = lruHash[key];
6131
6132                     if (!lruEntry) return;
6133
6134                     refresh(lruEntry);
6135                   }
6136
6137                   return data[key];
6138                 },
6139
6140
6141                 /**
6142                  * @ngdoc method
6143                  * @name $cacheFactory.Cache#remove
6144                  * @kind function
6145                  *
6146                  * @description
6147                  * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6148                  *
6149                  * @param {string} key the key of the entry to be removed
6150                  */
6151                 remove: function(key) {
6152                   if (capacity < Number.MAX_VALUE) {
6153                     var lruEntry = lruHash[key];
6154
6155                     if (!lruEntry) return;
6156
6157                     if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6158                     if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6159                     link(lruEntry.n,lruEntry.p);
6160
6161                     delete lruHash[key];
6162                   }
6163
6164                   if (!(key in data)) return;
6165
6166                   delete data[key];
6167                   size--;
6168                 },
6169
6170
6171                 /**
6172                  * @ngdoc method
6173                  * @name $cacheFactory.Cache#removeAll
6174                  * @kind function
6175                  *
6176                  * @description
6177                  * Clears the cache object of any entries.
6178                  */
6179                 removeAll: function() {
6180                   data = createMap();
6181                   size = 0;
6182                   lruHash = createMap();
6183                   freshEnd = staleEnd = null;
6184                 },
6185
6186
6187                 /**
6188                  * @ngdoc method
6189                  * @name $cacheFactory.Cache#destroy
6190                  * @kind function
6191                  *
6192                  * @description
6193                  * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6194                  * removing it from the {@link $cacheFactory $cacheFactory} set.
6195                  */
6196                 destroy: function() {
6197                   data = null;
6198                   stats = null;
6199                   lruHash = null;
6200                   delete caches[cacheId];
6201                 },
6202
6203
6204                 /**
6205                  * @ngdoc method
6206                  * @name $cacheFactory.Cache#info
6207                  * @kind function
6208                  *
6209                  * @description
6210                  * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6211                  *
6212                  * @returns {object} an object with the following properties:
6213                  *   <ul>
6214                  *     <li>**id**: the id of the cache instance</li>
6215                  *     <li>**size**: the number of entries kept in the cache instance</li>
6216                  *     <li>**...**: any additional properties from the options object when creating the
6217                  *       cache.</li>
6218                  *   </ul>
6219                  */
6220                 info: function() {
6221                   return extend({}, stats, {size: size});
6222                 }
6223               };
6224
6225
6226               /**
6227                * makes the `entry` the freshEnd of the LRU linked list
6228                */
6229               function refresh(entry) {
6230                 if (entry != freshEnd) {
6231                   if (!staleEnd) {
6232                     staleEnd = entry;
6233                   } else if (staleEnd == entry) {
6234                     staleEnd = entry.n;
6235                   }
6236
6237                   link(entry.n, entry.p);
6238                   link(entry, freshEnd);
6239                   freshEnd = entry;
6240                   freshEnd.n = null;
6241                 }
6242               }
6243
6244
6245               /**
6246                * bidirectionally links two entries of the LRU linked list
6247                */
6248               function link(nextEntry, prevEntry) {
6249                 if (nextEntry != prevEntry) {
6250                   if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6251                   if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6252                 }
6253               }
6254             }
6255
6256
6257           /**
6258            * @ngdoc method
6259            * @name $cacheFactory#info
6260            *
6261            * @description
6262            * Get information about all the caches that have been created
6263            *
6264            * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6265            */
6266             cacheFactory.info = function() {
6267               var info = {};
6268               forEach(caches, function(cache, cacheId) {
6269                 info[cacheId] = cache.info();
6270               });
6271               return info;
6272             };
6273
6274
6275           /**
6276            * @ngdoc method
6277            * @name $cacheFactory#get
6278            *
6279            * @description
6280            * Get access to a cache object by the `cacheId` used when it was created.
6281            *
6282            * @param {string} cacheId Name or id of a cache to access.
6283            * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6284            */
6285             cacheFactory.get = function(cacheId) {
6286               return caches[cacheId];
6287             };
6288
6289
6290             return cacheFactory;
6291           };
6292         }
6293
6294         /**
6295          * @ngdoc service
6296          * @name $templateCache
6297          *
6298          * @description
6299          * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6300          * can load templates directly into the cache in a `script` tag, or by consuming the
6301          * `$templateCache` service directly.
6302          *
6303          * Adding via the `script` tag:
6304          *
6305          * ```html
6306          *   <script type="text/ng-template" id="templateId.html">
6307          *     <p>This is the content of the template</p>
6308          *   </script>
6309          * ```
6310          *
6311          * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6312          * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6313          * element with ng-app attribute), otherwise the template will be ignored.
6314          *
6315          * Adding via the `$templateCache` service:
6316          *
6317          * ```js
6318          * var myApp = angular.module('myApp', []);
6319          * myApp.run(function($templateCache) {
6320          *   $templateCache.put('templateId.html', 'This is the content of the template');
6321          * });
6322          * ```
6323          *
6324          * To retrieve the template later, simply use it in your HTML:
6325          * ```html
6326          * <div ng-include=" 'templateId.html' "></div>
6327          * ```
6328          *
6329          * or get it via Javascript:
6330          * ```js
6331          * $templateCache.get('templateId.html')
6332          * ```
6333          *
6334          * See {@link ng.$cacheFactory $cacheFactory}.
6335          *
6336          */
6337         function $TemplateCacheProvider() {
6338           this.$get = ['$cacheFactory', function($cacheFactory) {
6339             return $cacheFactory('templates');
6340           }];
6341         }
6342
6343         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6344          *     Any commits to this file should be reviewed with security in mind.  *
6345          *   Changes to this file can potentially create security vulnerabilities. *
6346          *          An approval from 2 Core members with history of modifying      *
6347          *                         this file is required.                          *
6348          *                                                                         *
6349          *  Does the change somehow allow for arbitrary javascript to be executed? *
6350          *    Or allows for someone to change the prototype of built-in objects?   *
6351          *     Or gives undesired access to variables likes document or window?    *
6352          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6353
6354         /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6355          *
6356          * DOM-related variables:
6357          *
6358          * - "node" - DOM Node
6359          * - "element" - DOM Element or Node
6360          * - "$node" or "$element" - jqLite-wrapped node or element
6361          *
6362          *
6363          * Compiler related stuff:
6364          *
6365          * - "linkFn" - linking fn of a single directive
6366          * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6367          * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6368          * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6369          */
6370
6371
6372         /**
6373          * @ngdoc service
6374          * @name $compile
6375          * @kind function
6376          *
6377          * @description
6378          * Compiles an HTML string or DOM into a template and produces a template function, which
6379          * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6380          *
6381          * The compilation is a process of walking the DOM tree and matching DOM elements to
6382          * {@link ng.$compileProvider#directive directives}.
6383          *
6384          * <div class="alert alert-warning">
6385          * **Note:** This document is an in-depth reference of all directive options.
6386          * For a gentle introduction to directives with examples of common use cases,
6387          * see the {@link guide/directive directive guide}.
6388          * </div>
6389          *
6390          * ## Comprehensive Directive API
6391          *
6392          * There are many different options for a directive.
6393          *
6394          * The difference resides in the return value of the factory function.
6395          * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6396          * or just the `postLink` function (all other properties will have the default values).
6397          *
6398          * <div class="alert alert-success">
6399          * **Best Practice:** It's recommended to use the "directive definition object" form.
6400          * </div>
6401          *
6402          * Here's an example directive declared with a Directive Definition Object:
6403          *
6404          * ```js
6405          *   var myModule = angular.module(...);
6406          *
6407          *   myModule.directive('directiveName', function factory(injectables) {
6408          *     var directiveDefinitionObject = {
6409          *       priority: 0,
6410          *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6411          *       // or
6412          *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6413          *       transclude: false,
6414          *       restrict: 'A',
6415          *       templateNamespace: 'html',
6416          *       scope: false,
6417          *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6418          *       controllerAs: 'stringIdentifier',
6419          *       bindToController: false,
6420          *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6421          *       compile: function compile(tElement, tAttrs, transclude) {
6422          *         return {
6423          *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6424          *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6425          *         }
6426          *         // or
6427          *         // return function postLink( ... ) { ... }
6428          *       },
6429          *       // or
6430          *       // link: {
6431          *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6432          *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6433          *       // }
6434          *       // or
6435          *       // link: function postLink( ... ) { ... }
6436          *     };
6437          *     return directiveDefinitionObject;
6438          *   });
6439          * ```
6440          *
6441          * <div class="alert alert-warning">
6442          * **Note:** Any unspecified options will use the default value. You can see the default values below.
6443          * </div>
6444          *
6445          * Therefore the above can be simplified as:
6446          *
6447          * ```js
6448          *   var myModule = angular.module(...);
6449          *
6450          *   myModule.directive('directiveName', function factory(injectables) {
6451          *     var directiveDefinitionObject = {
6452          *       link: function postLink(scope, iElement, iAttrs) { ... }
6453          *     };
6454          *     return directiveDefinitionObject;
6455          *     // or
6456          *     // return function postLink(scope, iElement, iAttrs) { ... }
6457          *   });
6458          * ```
6459          *
6460          *
6461          *
6462          * ### Directive Definition Object
6463          *
6464          * The directive definition object provides instructions to the {@link ng.$compile
6465          * compiler}. The attributes are:
6466          *
6467          * #### `multiElement`
6468          * When this property is set to true, the HTML compiler will collect DOM nodes between
6469          * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6470          * together as the directive elements. It is recommended that this feature be used on directives
6471          * which are not strictly behavioural (such as {@link ngClick}), and which
6472          * do not manipulate or replace child nodes (such as {@link ngInclude}).
6473          *
6474          * #### `priority`
6475          * When there are multiple directives defined on a single DOM element, sometimes it
6476          * is necessary to specify the order in which the directives are applied. The `priority` is used
6477          * to sort the directives before their `compile` functions get called. Priority is defined as a
6478          * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6479          * are also run in priority order, but post-link functions are run in reverse order. The order
6480          * of directives with the same priority is undefined. The default priority is `0`.
6481          *
6482          * #### `terminal`
6483          * If set to true then the current `priority` will be the last set of directives
6484          * which will execute (any directives at the current priority will still execute
6485          * as the order of execution on same `priority` is undefined). Note that expressions
6486          * and other directives used in the directive's template will also be excluded from execution.
6487          *
6488          * #### `scope`
6489          * The scope property can be `true`, an object or a falsy value:
6490          *
6491          * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6492          *
6493          * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6494          * the directive's element. If multiple directives on the same element request a new scope,
6495          * only one new scope is created. The new scope rule does not apply for the root of the template
6496          * since the root of the template always gets a new scope.
6497          *
6498          * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6499          * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6500          * scope. This is useful when creating reusable components, which should not accidentally read or modify
6501          * data in the parent scope.
6502          *
6503          * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6504          * directive's element. These local properties are useful for aliasing values for templates. The keys in
6505          * the object hash map to the name of the property on the isolate scope; the values define how the property
6506          * is bound to the parent scope, via matching attributes on the directive's element:
6507          *
6508          * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6509          *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6510          *   attribute name is assumed to be the same as the local name.
6511          *   Given `<widget my-attr="hello {{name}}">` and widget definition
6512          *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6513          *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6514          *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6515          *   component scope).
6516          *
6517          * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6518          *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6519          *   name is specified then the attribute name is assumed to be the same as the local name.
6520          *   Given `<widget my-attr="parentModel">` and widget definition of
6521          *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6522          *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6523          *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6524          *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6525          *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6526          *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6527          *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6528          *
6529          * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6530          *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6531          *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6532          *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6533          *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6534          *   pass data from the isolated scope via an expression to the parent scope, this can be
6535          *   done by passing a map of local variable names and values into the expression wrapper fn.
6536          *   For example, if the expression is `increment(amount)` then we can specify the amount value
6537          *   by calling the `localFn` as `localFn({amount: 22})`.
6538          *
6539          * In general it's possible to apply more than one directive to one element, but there might be limitations
6540          * depending on the type of scope required by the directives. The following points will help explain these limitations.
6541          * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6542          *
6543          * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6544          * * **child scope** + **no scope** =>  Both directives will share one single child scope
6545          * * **child scope** + **child scope** =>  Both directives will share one single child scope
6546          * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6547          * its parent's scope
6548          * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6549          * be applied to the same element.
6550          * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6551          * cannot be applied to the same element.
6552          *
6553          *
6554          * #### `bindToController`
6555          * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6556          * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6557          * is instantiated, the initial values of the isolate scope bindings are already available.
6558          *
6559          * #### `controller`
6560          * Controller constructor function. The controller is instantiated before the
6561          * pre-linking phase and can be accessed by other directives (see
6562          * `require` attribute). This allows the directives to communicate with each other and augment
6563          * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6564          *
6565          * * `$scope` - Current scope associated with the element
6566          * * `$element` - Current element
6567          * * `$attrs` - Current attributes object for the element
6568          * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6569          *   `function([scope], cloneLinkingFn, futureParentElement)`.
6570          *    * `scope`: optional argument to override the scope.
6571          *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6572          *    * `futureParentElement`:
6573          *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6574          *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6575          *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6576          *          and when the `cloneLinkinFn` is passed,
6577          *          as those elements need to created and cloned in a special way when they are defined outside their
6578          *          usual containers (e.g. like `<svg>`).
6579          *        * See also the `directive.templateNamespace` property.
6580          *
6581          *
6582          * #### `require`
6583          * Require another directive and inject its controller as the fourth argument to the linking function. The
6584          * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6585          * injected argument will be an array in corresponding order. If no such directive can be
6586          * found, or if the directive does not have a controller, then an error is raised (unless no link function
6587          * is specified, in which case error checking is skipped). The name can be prefixed with:
6588          *
6589          * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6590          * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6591          * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6592          * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6593          * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6594          *   `null` to the `link` fn if not found.
6595          * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6596          *   `null` to the `link` fn if not found.
6597          *
6598          *
6599          * #### `controllerAs`
6600          * Identifier name for a reference to the controller in the directive's scope.
6601          * This allows the controller to be referenced from the directive template. This is especially
6602          * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6603          * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6604          * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6605          *
6606          *
6607          * #### `restrict`
6608          * String of subset of `EACM` which restricts the directive to a specific directive
6609          * declaration style. If omitted, the defaults (elements and attributes) are used.
6610          *
6611          * * `E` - Element name (default): `<my-directive></my-directive>`
6612          * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6613          * * `C` - Class: `<div class="my-directive: exp;"></div>`
6614          * * `M` - Comment: `<!-- directive: my-directive exp -->`
6615          *
6616          *
6617          * #### `templateNamespace`
6618          * String representing the document type used by the markup in the template.
6619          * AngularJS needs this information as those elements need to be created and cloned
6620          * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6621          *
6622          * * `html` - All root nodes in the template are HTML. Root nodes may also be
6623          *   top-level elements such as `<svg>` or `<math>`.
6624          * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6625          * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6626          *
6627          * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6628          *
6629          * #### `template`
6630          * HTML markup that may:
6631          * * Replace the contents of the directive's element (default).
6632          * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6633          * * Wrap the contents of the directive's element (if `transclude` is true).
6634          *
6635          * Value may be:
6636          *
6637          * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6638          * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6639          *   function api below) and returns a string value.
6640          *
6641          *
6642          * #### `templateUrl`
6643          * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6644          *
6645          * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6646          * for later when the template has been resolved.  In the meantime it will continue to compile and link
6647          * sibling and parent elements as though this element had not contained any directives.
6648          *
6649          * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6650          * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6651          * case when only one deeply nested directive has `templateUrl`.
6652          *
6653          * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6654          *
6655          * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6656          * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6657          * a string value representing the url.  In either case, the template URL is passed through {@link
6658          * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6659          *
6660          *
6661          * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6662          * specify what the template should replace. Defaults to `false`.
6663          *
6664          * * `true` - the template will replace the directive's element.
6665          * * `false` - the template will replace the contents of the directive's element.
6666          *
6667          * The replacement process migrates all of the attributes / classes from the old element to the new
6668          * one. See the {@link guide/directive#template-expanding-directive
6669          * Directives Guide} for an example.
6670          *
6671          * There are very few scenarios where element replacement is required for the application function,
6672          * the main one being reusable custom components that are used within SVG contexts
6673          * (because SVG doesn't work with custom elements in the DOM tree).
6674          *
6675          * #### `transclude`
6676          * Extract the contents of the element where the directive appears and make it available to the directive.
6677          * The contents are compiled and provided to the directive as a **transclusion function**. See the
6678          * {@link $compile#transclusion Transclusion} section below.
6679          *
6680          * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6681          * directive's element or the entire element:
6682          *
6683          * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6684          * * `'element'` - transclude the whole of the directive's element including any directives on this
6685          *   element that defined at a lower priority than this directive. When used, the `template`
6686          *   property is ignored.
6687          *
6688          *
6689          * #### `compile`
6690          *
6691          * ```js
6692          *   function compile(tElement, tAttrs, transclude) { ... }
6693          * ```
6694          *
6695          * The compile function deals with transforming the template DOM. Since most directives do not do
6696          * template transformation, it is not used often. The compile function takes the following arguments:
6697          *
6698          *   * `tElement` - template element - The element where the directive has been declared. It is
6699          *     safe to do template transformation on the element and child elements only.
6700          *
6701          *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6702          *     between all directive compile functions.
6703          *
6704          *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6705          *
6706          * <div class="alert alert-warning">
6707          * **Note:** The template instance and the link instance may be different objects if the template has
6708          * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6709          * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6710          * should be done in a linking function rather than in a compile function.
6711          * </div>
6712
6713          * <div class="alert alert-warning">
6714          * **Note:** The compile function cannot handle directives that recursively use themselves in their
6715          * own templates or compile functions. Compiling these directives results in an infinite loop and a
6716          * stack overflow errors.
6717          *
6718          * This can be avoided by manually using $compile in the postLink function to imperatively compile
6719          * a directive's template instead of relying on automatic template compilation via `template` or
6720          * `templateUrl` declaration or manual compilation inside the compile function.
6721          * </div>
6722          *
6723          * <div class="alert alert-danger">
6724          * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6725          *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6726          *   to the link function instead.
6727          * </div>
6728
6729          * A compile function can have a return value which can be either a function or an object.
6730          *
6731          * * returning a (post-link) function - is equivalent to registering the linking function via the
6732          *   `link` property of the config object when the compile function is empty.
6733          *
6734          * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6735          *   control when a linking function should be called during the linking phase. See info about
6736          *   pre-linking and post-linking functions below.
6737          *
6738          *
6739          * #### `link`
6740          * This property is used only if the `compile` property is not defined.
6741          *
6742          * ```js
6743          *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6744          * ```
6745          *
6746          * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6747          * executed after the template has been cloned. This is where most of the directive logic will be
6748          * put.
6749          *
6750          *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6751          *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6752          *
6753          *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6754          *     manipulate the children of the element only in `postLink` function since the children have
6755          *     already been linked.
6756          *
6757          *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6758          *     between all directive linking functions.
6759          *
6760          *   * `controller` - the directive's required controller instance(s) - Instances are shared
6761          *     among all directives, which allows the directives to use the controllers as a communication
6762          *     channel. The exact value depends on the directive's `require` property:
6763          *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6764          *       * `string`: the controller instance
6765          *       * `array`: array of controller instances
6766          *
6767          *     If a required controller cannot be found, and it is optional, the instance is `null`,
6768          *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6769          *
6770          *     Note that you can also require the directive's own controller - it will be made available like
6771          *     any other controller.
6772          *
6773          *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6774          *     This is the same as the `$transclude`
6775          *     parameter of directive controllers, see there for details.
6776          *     `function([scope], cloneLinkingFn, futureParentElement)`.
6777          *
6778          * #### Pre-linking function
6779          *
6780          * Executed before the child elements are linked. Not safe to do DOM transformation since the
6781          * compiler linking function will fail to locate the correct elements for linking.
6782          *
6783          * #### Post-linking function
6784          *
6785          * Executed after the child elements are linked.
6786          *
6787          * Note that child elements that contain `templateUrl` directives will not have been compiled
6788          * and linked since they are waiting for their template to load asynchronously and their own
6789          * compilation and linking has been suspended until that occurs.
6790          *
6791          * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6792          * for their async templates to be resolved.
6793          *
6794          *
6795          * ### Transclusion
6796          *
6797          * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6798          * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6799          * scope from where they were taken.
6800          *
6801          * Transclusion is used (often with {@link ngTransclude}) to insert the
6802          * original contents of a directive's element into a specified place in the template of the directive.
6803          * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6804          * content has access to the properties on the scope from which it was taken, even if the directive
6805          * has isolated scope.
6806          * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6807          *
6808          * This makes it possible for the widget to have private state for its template, while the transcluded
6809          * content has access to its originating scope.
6810          *
6811          * <div class="alert alert-warning">
6812          * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6813          * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6814          * Testing Transclusion Directives}.
6815          * </div>
6816          *
6817          * #### Transclusion Functions
6818          *
6819          * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6820          * function** to the directive's `link` function and `controller`. This transclusion function is a special
6821          * **linking function** that will return the compiled contents linked to a new transclusion scope.
6822          *
6823          * <div class="alert alert-info">
6824          * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6825          * ngTransclude will deal with it for us.
6826          * </div>
6827          *
6828          * If you want to manually control the insertion and removal of the transcluded content in your directive
6829          * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6830          * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6831          *
6832          * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6833          * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6834          * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6835          *
6836          * <div class="alert alert-info">
6837          * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6838          * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6839          * </div>
6840          *
6841          * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6842          * attach function**:
6843          *
6844          * ```js
6845          * var transcludedContent, transclusionScope;
6846          *
6847          * $transclude(function(clone, scope) {
6848          *   element.append(clone);
6849          *   transcludedContent = clone;
6850          *   transclusionScope = scope;
6851          * });
6852          * ```
6853          *
6854          * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6855          * associated transclusion scope:
6856          *
6857          * ```js
6858          * transcludedContent.remove();
6859          * transclusionScope.$destroy();
6860          * ```
6861          *
6862          * <div class="alert alert-info">
6863          * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6864          * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6865          * then you are also responsible for calling `$destroy` on the transclusion scope.
6866          * </div>
6867          *
6868          * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6869          * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6870          * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6871          *
6872          *
6873          * #### Transclusion Scopes
6874          *
6875          * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6876          * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6877          * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6878          * was taken.
6879          *
6880          * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6881          * like this:
6882          *
6883          * ```html
6884          * <div ng-app>
6885          *   <div isolate>
6886          *     <div transclusion>
6887          *     </div>
6888          *   </div>
6889          * </div>
6890          * ```
6891          *
6892          * The `$parent` scope hierarchy will look like this:
6893          *
6894          * ```
6895          * - $rootScope
6896          *   - isolate
6897          *     - transclusion
6898          * ```
6899          *
6900          * but the scopes will inherit prototypically from different scopes to their `$parent`.
6901          *
6902          * ```
6903          * - $rootScope
6904          *   - transclusion
6905          * - isolate
6906          * ```
6907          *
6908          *
6909          * ### Attributes
6910          *
6911          * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6912          * `link()` or `compile()` functions. It has a variety of uses.
6913          *
6914          * accessing *Normalized attribute names:*
6915          * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6916          * the attributes object allows for normalized access to
6917          *   the attributes.
6918          *
6919          * * *Directive inter-communication:* All directives share the same instance of the attributes
6920          *   object which allows the directives to use the attributes object as inter directive
6921          *   communication.
6922          *
6923          * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6924          *   allowing other directives to read the interpolated value.
6925          *
6926          * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6927          *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6928          *   the only way to easily get the actual value because during the linking phase the interpolation
6929          *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6930          *
6931          * ```js
6932          * function linkingFn(scope, elm, attrs, ctrl) {
6933          *   // get the attribute value
6934          *   console.log(attrs.ngModel);
6935          *
6936          *   // change the attribute
6937          *   attrs.$set('ngModel', 'new value');
6938          *
6939          *   // observe changes to interpolated attribute
6940          *   attrs.$observe('ngModel', function(value) {
6941          *     console.log('ngModel has changed value to ' + value);
6942          *   });
6943          * }
6944          * ```
6945          *
6946          * ## Example
6947          *
6948          * <div class="alert alert-warning">
6949          * **Note**: Typically directives are registered with `module.directive`. The example below is
6950          * to illustrate how `$compile` works.
6951          * </div>
6952          *
6953          <example module="compileExample">
6954            <file name="index.html">
6955             <script>
6956               angular.module('compileExample', [], function($compileProvider) {
6957                 // configure new 'compile' directive by passing a directive
6958                 // factory function. The factory function injects the '$compile'
6959                 $compileProvider.directive('compile', function($compile) {
6960                   // directive factory creates a link function
6961                   return function(scope, element, attrs) {
6962                     scope.$watch(
6963                       function(scope) {
6964                          // watch the 'compile' expression for changes
6965                         return scope.$eval(attrs.compile);
6966                       },
6967                       function(value) {
6968                         // when the 'compile' expression changes
6969                         // assign it into the current DOM
6970                         element.html(value);
6971
6972                         // compile the new DOM and link it to the current
6973                         // scope.
6974                         // NOTE: we only compile .childNodes so that
6975                         // we don't get into infinite loop compiling ourselves
6976                         $compile(element.contents())(scope);
6977                       }
6978                     );
6979                   };
6980                 });
6981               })
6982               .controller('GreeterController', ['$scope', function($scope) {
6983                 $scope.name = 'Angular';
6984                 $scope.html = 'Hello {{name}}';
6985               }]);
6986             </script>
6987             <div ng-controller="GreeterController">
6988               <input ng-model="name"> <br/>
6989               <textarea ng-model="html"></textarea> <br/>
6990               <div compile="html"></div>
6991             </div>
6992            </file>
6993            <file name="protractor.js" type="protractor">
6994              it('should auto compile', function() {
6995                var textarea = $('textarea');
6996                var output = $('div[compile]');
6997                // The initial state reads 'Hello Angular'.
6998                expect(output.getText()).toBe('Hello Angular');
6999                textarea.clear();
7000                textarea.sendKeys('{{name}}!');
7001                expect(output.getText()).toBe('Angular!');
7002              });
7003            </file>
7004          </example>
7005
7006          *
7007          *
7008          * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7009          * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7010          *
7011          * <div class="alert alert-danger">
7012          * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7013          *   e.g. will not use the right outer scope. Please pass the transclude function as a
7014          *   `parentBoundTranscludeFn` to the link function instead.
7015          * </div>
7016          *
7017          * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7018          *                 root element(s), not their children)
7019          * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7020          * (a DOM element/tree) to a scope. Where:
7021          *
7022          *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7023          *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7024          *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7025          *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7026          *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7027          *
7028          *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7029          *      * `scope` - is the current scope with which the linking function is working with.
7030          *
7031          *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7032          *  keys may be used to control linking behavior:
7033          *
7034          *      * `parentBoundTranscludeFn` - the transclude function made available to
7035          *        directives; if given, it will be passed through to the link functions of
7036          *        directives found in `element` during compilation.
7037          *      * `transcludeControllers` - an object hash with keys that map controller names
7038          *        to controller instances; if given, it will make the controllers
7039          *        available to directives.
7040          *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7041          *        the cloned elements; only needed for transcludes that are allowed to contain non html
7042          *        elements (e.g. SVG elements). See also the directive.controller property.
7043          *
7044          * Calling the linking function returns the element of the template. It is either the original
7045          * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7046          *
7047          * After linking the view is not updated until after a call to $digest which typically is done by
7048          * Angular automatically.
7049          *
7050          * If you need access to the bound view, there are two ways to do it:
7051          *
7052          * - If you are not asking the linking function to clone the template, create the DOM element(s)
7053          *   before you send them to the compiler and keep this reference around.
7054          *   ```js
7055          *     var element = $compile('<p>{{total}}</p>')(scope);
7056          *   ```
7057          *
7058          * - if on the other hand, you need the element to be cloned, the view reference from the original
7059          *   example would not point to the clone, but rather to the original template that was cloned. In
7060          *   this case, you can access the clone via the cloneAttachFn:
7061          *   ```js
7062          *     var templateElement = angular.element('<p>{{total}}</p>'),
7063          *         scope = ....;
7064          *
7065          *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7066          *       //attach the clone to DOM document at the right place
7067          *     });
7068          *
7069          *     //now we have reference to the cloned DOM via `clonedElement`
7070          *   ```
7071          *
7072          *
7073          * For information on how the compiler works, see the
7074          * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7075          */
7076
7077         var $compileMinErr = minErr('$compile');
7078
7079         /**
7080          * @ngdoc provider
7081          * @name $compileProvider
7082          *
7083          * @description
7084          */
7085         $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7086         function $CompileProvider($provide, $$sanitizeUriProvider) {
7087           var hasDirectives = {},
7088               Suffix = 'Directive',
7089               COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7090               CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7091               ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7092               REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7093
7094           // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7095           // The assumption is that future DOM event attribute names will begin with
7096           // 'on' and be composed of only English letters.
7097           var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7098
7099           function parseIsolateBindings(scope, directiveName, isController) {
7100             var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7101
7102             var bindings = {};
7103
7104             forEach(scope, function(definition, scopeName) {
7105               var match = definition.match(LOCAL_REGEXP);
7106
7107               if (!match) {
7108                 throw $compileMinErr('iscp',
7109                     "Invalid {3} for directive '{0}'." +
7110                     " Definition: {... {1}: '{2}' ...}",
7111                     directiveName, scopeName, definition,
7112                     (isController ? "controller bindings definition" :
7113                     "isolate scope definition"));
7114               }
7115
7116               bindings[scopeName] = {
7117                 mode: match[1][0],
7118                 collection: match[2] === '*',
7119                 optional: match[3] === '?',
7120                 attrName: match[4] || scopeName
7121               };
7122             });
7123
7124             return bindings;
7125           }
7126
7127           function parseDirectiveBindings(directive, directiveName) {
7128             var bindings = {
7129               isolateScope: null,
7130               bindToController: null
7131             };
7132             if (isObject(directive.scope)) {
7133               if (directive.bindToController === true) {
7134                 bindings.bindToController = parseIsolateBindings(directive.scope,
7135                                                                  directiveName, true);
7136                 bindings.isolateScope = {};
7137               } else {
7138                 bindings.isolateScope = parseIsolateBindings(directive.scope,
7139                                                              directiveName, false);
7140               }
7141             }
7142             if (isObject(directive.bindToController)) {
7143               bindings.bindToController =
7144                   parseIsolateBindings(directive.bindToController, directiveName, true);
7145             }
7146             if (isObject(bindings.bindToController)) {
7147               var controller = directive.controller;
7148               var controllerAs = directive.controllerAs;
7149               if (!controller) {
7150                 // There is no controller, there may or may not be a controllerAs property
7151                 throw $compileMinErr('noctrl',
7152                       "Cannot bind to controller without directive '{0}'s controller.",
7153                       directiveName);
7154               } else if (!identifierForController(controller, controllerAs)) {
7155                 // There is a controller, but no identifier or controllerAs property
7156                 throw $compileMinErr('noident',
7157                       "Cannot bind to controller without identifier for directive '{0}'.",
7158                       directiveName);
7159               }
7160             }
7161             return bindings;
7162           }
7163
7164           function assertValidDirectiveName(name) {
7165             var letter = name.charAt(0);
7166             if (!letter || letter !== lowercase(letter)) {
7167               throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7168             }
7169             if (name !== name.trim()) {
7170               throw $compileMinErr('baddir',
7171                     "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7172                     name);
7173             }
7174           }
7175
7176           /**
7177            * @ngdoc method
7178            * @name $compileProvider#directive
7179            * @kind function
7180            *
7181            * @description
7182            * Register a new directive with the compiler.
7183            *
7184            * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7185            *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7186            *    names and the values are the factories.
7187            * @param {Function|Array} directiveFactory An injectable directive factory function. See
7188            *    {@link guide/directive} for more info.
7189            * @returns {ng.$compileProvider} Self for chaining.
7190            */
7191            this.directive = function registerDirective(name, directiveFactory) {
7192             assertNotHasOwnProperty(name, 'directive');
7193             if (isString(name)) {
7194               assertValidDirectiveName(name);
7195               assertArg(directiveFactory, 'directiveFactory');
7196               if (!hasDirectives.hasOwnProperty(name)) {
7197                 hasDirectives[name] = [];
7198                 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7199                   function($injector, $exceptionHandler) {
7200                     var directives = [];
7201                     forEach(hasDirectives[name], function(directiveFactory, index) {
7202                       try {
7203                         var directive = $injector.invoke(directiveFactory);
7204                         if (isFunction(directive)) {
7205                           directive = { compile: valueFn(directive) };
7206                         } else if (!directive.compile && directive.link) {
7207                           directive.compile = valueFn(directive.link);
7208                         }
7209                         directive.priority = directive.priority || 0;
7210                         directive.index = index;
7211                         directive.name = directive.name || name;
7212                         directive.require = directive.require || (directive.controller && directive.name);
7213                         directive.restrict = directive.restrict || 'EA';
7214                         var bindings = directive.$$bindings =
7215                             parseDirectiveBindings(directive, directive.name);
7216                         if (isObject(bindings.isolateScope)) {
7217                           directive.$$isolateBindings = bindings.isolateScope;
7218                         }
7219                         directive.$$moduleName = directiveFactory.$$moduleName;
7220                         directives.push(directive);
7221                       } catch (e) {
7222                         $exceptionHandler(e);
7223                       }
7224                     });
7225                     return directives;
7226                   }]);
7227               }
7228               hasDirectives[name].push(directiveFactory);
7229             } else {
7230               forEach(name, reverseParams(registerDirective));
7231             }
7232             return this;
7233           };
7234
7235
7236           /**
7237            * @ngdoc method
7238            * @name $compileProvider#aHrefSanitizationWhitelist
7239            * @kind function
7240            *
7241            * @description
7242            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7243            * urls during a[href] sanitization.
7244            *
7245            * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7246            *
7247            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7248            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7249            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7250            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7251            *
7252            * @param {RegExp=} regexp New regexp to whitelist urls with.
7253            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7254            *    chaining otherwise.
7255            */
7256           this.aHrefSanitizationWhitelist = function(regexp) {
7257             if (isDefined(regexp)) {
7258               $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7259               return this;
7260             } else {
7261               return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7262             }
7263           };
7264
7265
7266           /**
7267            * @ngdoc method
7268            * @name $compileProvider#imgSrcSanitizationWhitelist
7269            * @kind function
7270            *
7271            * @description
7272            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7273            * urls during img[src] sanitization.
7274            *
7275            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7276            *
7277            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7278            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7279            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7280            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7281            *
7282            * @param {RegExp=} regexp New regexp to whitelist urls with.
7283            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7284            *    chaining otherwise.
7285            */
7286           this.imgSrcSanitizationWhitelist = function(regexp) {
7287             if (isDefined(regexp)) {
7288               $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7289               return this;
7290             } else {
7291               return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7292             }
7293           };
7294
7295           /**
7296            * @ngdoc method
7297            * @name  $compileProvider#debugInfoEnabled
7298            *
7299            * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7300            * current debugInfoEnabled state
7301            * @returns {*} current value if used as getter or itself (chaining) if used as setter
7302            *
7303            * @kind function
7304            *
7305            * @description
7306            * Call this method to enable/disable various debug runtime information in the compiler such as adding
7307            * binding information and a reference to the current scope on to DOM elements.
7308            * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7309            * * `ng-binding` CSS class
7310            * * `$binding` data property containing an array of the binding expressions
7311            *
7312            * You may want to disable this in production for a significant performance boost. See
7313            * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7314            *
7315            * The default value is true.
7316            */
7317           var debugInfoEnabled = true;
7318           this.debugInfoEnabled = function(enabled) {
7319             if (isDefined(enabled)) {
7320               debugInfoEnabled = enabled;
7321               return this;
7322             }
7323             return debugInfoEnabled;
7324           };
7325
7326           this.$get = [
7327                     '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7328                     '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7329             function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7330                      $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7331
7332             var Attributes = function(element, attributesToCopy) {
7333               if (attributesToCopy) {
7334                 var keys = Object.keys(attributesToCopy);
7335                 var i, l, key;
7336
7337                 for (i = 0, l = keys.length; i < l; i++) {
7338                   key = keys[i];
7339                   this[key] = attributesToCopy[key];
7340                 }
7341               } else {
7342                 this.$attr = {};
7343               }
7344
7345               this.$$element = element;
7346             };
7347
7348             Attributes.prototype = {
7349               /**
7350                * @ngdoc method
7351                * @name $compile.directive.Attributes#$normalize
7352                * @kind function
7353                *
7354                * @description
7355                * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7356                * `data-`) to its normalized, camelCase form.
7357                *
7358                * Also there is special case for Moz prefix starting with upper case letter.
7359                *
7360                * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7361                *
7362                * @param {string} name Name to normalize
7363                */
7364               $normalize: directiveNormalize,
7365
7366
7367               /**
7368                * @ngdoc method
7369                * @name $compile.directive.Attributes#$addClass
7370                * @kind function
7371                *
7372                * @description
7373                * Adds the CSS class value specified by the classVal parameter to the element. If animations
7374                * are enabled then an animation will be triggered for the class addition.
7375                *
7376                * @param {string} classVal The className value that will be added to the element
7377                */
7378               $addClass: function(classVal) {
7379                 if (classVal && classVal.length > 0) {
7380                   $animate.addClass(this.$$element, classVal);
7381                 }
7382               },
7383
7384               /**
7385                * @ngdoc method
7386                * @name $compile.directive.Attributes#$removeClass
7387                * @kind function
7388                *
7389                * @description
7390                * Removes the CSS class value specified by the classVal parameter from the element. If
7391                * animations are enabled then an animation will be triggered for the class removal.
7392                *
7393                * @param {string} classVal The className value that will be removed from the element
7394                */
7395               $removeClass: function(classVal) {
7396                 if (classVal && classVal.length > 0) {
7397                   $animate.removeClass(this.$$element, classVal);
7398                 }
7399               },
7400
7401               /**
7402                * @ngdoc method
7403                * @name $compile.directive.Attributes#$updateClass
7404                * @kind function
7405                *
7406                * @description
7407                * Adds and removes the appropriate CSS class values to the element based on the difference
7408                * between the new and old CSS class values (specified as newClasses and oldClasses).
7409                *
7410                * @param {string} newClasses The current CSS className value
7411                * @param {string} oldClasses The former CSS className value
7412                */
7413               $updateClass: function(newClasses, oldClasses) {
7414                 var toAdd = tokenDifference(newClasses, oldClasses);
7415                 if (toAdd && toAdd.length) {
7416                   $animate.addClass(this.$$element, toAdd);
7417                 }
7418
7419                 var toRemove = tokenDifference(oldClasses, newClasses);
7420                 if (toRemove && toRemove.length) {
7421                   $animate.removeClass(this.$$element, toRemove);
7422                 }
7423               },
7424
7425               /**
7426                * Set a normalized attribute on the element in a way such that all directives
7427                * can share the attribute. This function properly handles boolean attributes.
7428                * @param {string} key Normalized key. (ie ngAttribute)
7429                * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7430                * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7431                *     Defaults to true.
7432                * @param {string=} attrName Optional none normalized name. Defaults to key.
7433                */
7434               $set: function(key, value, writeAttr, attrName) {
7435                 // TODO: decide whether or not to throw an error if "class"
7436                 //is set through this function since it may cause $updateClass to
7437                 //become unstable.
7438
7439                 var node = this.$$element[0],
7440                     booleanKey = getBooleanAttrName(node, key),
7441                     aliasedKey = getAliasedAttrName(key),
7442                     observer = key,
7443                     nodeName;
7444
7445                 if (booleanKey) {
7446                   this.$$element.prop(key, value);
7447                   attrName = booleanKey;
7448                 } else if (aliasedKey) {
7449                   this[aliasedKey] = value;
7450                   observer = aliasedKey;
7451                 }
7452
7453                 this[key] = value;
7454
7455                 // translate normalized key to actual key
7456                 if (attrName) {
7457                   this.$attr[key] = attrName;
7458                 } else {
7459                   attrName = this.$attr[key];
7460                   if (!attrName) {
7461                     this.$attr[key] = attrName = snake_case(key, '-');
7462                   }
7463                 }
7464
7465                 nodeName = nodeName_(this.$$element);
7466
7467                 if ((nodeName === 'a' && key === 'href') ||
7468                     (nodeName === 'img' && key === 'src')) {
7469                   // sanitize a[href] and img[src] values
7470                   this[key] = value = $$sanitizeUri(value, key === 'src');
7471                 } else if (nodeName === 'img' && key === 'srcset') {
7472                   // sanitize img[srcset] values
7473                   var result = "";
7474
7475                   // first check if there are spaces because it's not the same pattern
7476                   var trimmedSrcset = trim(value);
7477                   //                (   999x   ,|   999w   ,|   ,|,   )
7478                   var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7479                   var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7480
7481                   // split srcset into tuple of uri and descriptor except for the last item
7482                   var rawUris = trimmedSrcset.split(pattern);
7483
7484                   // for each tuples
7485                   var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7486                   for (var i = 0; i < nbrUrisWith2parts; i++) {
7487                     var innerIdx = i * 2;
7488                     // sanitize the uri
7489                     result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7490                     // add the descriptor
7491                     result += (" " + trim(rawUris[innerIdx + 1]));
7492                   }
7493
7494                   // split the last item into uri and descriptor
7495                   var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7496
7497                   // sanitize the last uri
7498                   result += $$sanitizeUri(trim(lastTuple[0]), true);
7499
7500                   // and add the last descriptor if any
7501                   if (lastTuple.length === 2) {
7502                     result += (" " + trim(lastTuple[1]));
7503                   }
7504                   this[key] = value = result;
7505                 }
7506
7507                 if (writeAttr !== false) {
7508                   if (value === null || isUndefined(value)) {
7509                     this.$$element.removeAttr(attrName);
7510                   } else {
7511                     this.$$element.attr(attrName, value);
7512                   }
7513                 }
7514
7515                 // fire observers
7516                 var $$observers = this.$$observers;
7517                 $$observers && forEach($$observers[observer], function(fn) {
7518                   try {
7519                     fn(value);
7520                   } catch (e) {
7521                     $exceptionHandler(e);
7522                   }
7523                 });
7524               },
7525
7526
7527               /**
7528                * @ngdoc method
7529                * @name $compile.directive.Attributes#$observe
7530                * @kind function
7531                *
7532                * @description
7533                * Observes an interpolated attribute.
7534                *
7535                * The observer function will be invoked once during the next `$digest` following
7536                * compilation. The observer is then invoked whenever the interpolated value
7537                * changes.
7538                *
7539                * @param {string} key Normalized key. (ie ngAttribute) .
7540                * @param {function(interpolatedValue)} fn Function that will be called whenever
7541                         the interpolated value of the attribute changes.
7542                *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7543                * @returns {function()} Returns a deregistration function for this observer.
7544                */
7545               $observe: function(key, fn) {
7546                 var attrs = this,
7547                     $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7548                     listeners = ($$observers[key] || ($$observers[key] = []));
7549
7550                 listeners.push(fn);
7551                 $rootScope.$evalAsync(function() {
7552                   if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7553                     // no one registered attribute interpolation function, so lets call it manually
7554                     fn(attrs[key]);
7555                   }
7556                 });
7557
7558                 return function() {
7559                   arrayRemove(listeners, fn);
7560                 };
7561               }
7562             };
7563
7564
7565             function safeAddClass($element, className) {
7566               try {
7567                 $element.addClass(className);
7568               } catch (e) {
7569                 // ignore, since it means that we are trying to set class on
7570                 // SVG element, where class name is read-only.
7571               }
7572             }
7573
7574
7575             var startSymbol = $interpolate.startSymbol(),
7576                 endSymbol = $interpolate.endSymbol(),
7577                 denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7578                     ? identity
7579                     : function denormalizeTemplate(template) {
7580                       return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7581                 },
7582                 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7583             var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7584
7585             compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7586               var bindings = $element.data('$binding') || [];
7587
7588               if (isArray(binding)) {
7589                 bindings = bindings.concat(binding);
7590               } else {
7591                 bindings.push(binding);
7592               }
7593
7594               $element.data('$binding', bindings);
7595             } : noop;
7596
7597             compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7598               safeAddClass($element, 'ng-binding');
7599             } : noop;
7600
7601             compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7602               var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7603               $element.data(dataName, scope);
7604             } : noop;
7605
7606             compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7607               safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7608             } : noop;
7609
7610             return compile;
7611
7612             //================================
7613
7614             function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7615                                 previousCompileContext) {
7616               if (!($compileNodes instanceof jqLite)) {
7617                 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7618                 // modify it.
7619                 $compileNodes = jqLite($compileNodes);
7620               }
7621               // We can not compile top level text elements since text nodes can be merged and we will
7622               // not be able to attach scope data to them, so we will wrap them in <span>
7623               forEach($compileNodes, function(node, index) {
7624                 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7625                   $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7626                 }
7627               });
7628               var compositeLinkFn =
7629                       compileNodes($compileNodes, transcludeFn, $compileNodes,
7630                                    maxPriority, ignoreDirective, previousCompileContext);
7631               compile.$$addScopeClass($compileNodes);
7632               var namespace = null;
7633               return function publicLinkFn(scope, cloneConnectFn, options) {
7634                 assertArg(scope, 'scope');
7635
7636                 if (previousCompileContext && previousCompileContext.needsNewScope) {
7637                   // A parent directive did a replace and a directive on this element asked
7638                   // for transclusion, which caused us to lose a layer of element on which
7639                   // we could hold the new transclusion scope, so we will create it manually
7640                   // here.
7641                   scope = scope.$parent.$new();
7642                 }
7643
7644                 options = options || {};
7645                 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7646                   transcludeControllers = options.transcludeControllers,
7647                   futureParentElement = options.futureParentElement;
7648
7649                 // When `parentBoundTranscludeFn` is passed, it is a
7650                 // `controllersBoundTransclude` function (it was previously passed
7651                 // as `transclude` to directive.link) so we must unwrap it to get
7652                 // its `boundTranscludeFn`
7653                 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7654                   parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7655                 }
7656
7657                 if (!namespace) {
7658                   namespace = detectNamespaceForChildElements(futureParentElement);
7659                 }
7660                 var $linkNode;
7661                 if (namespace !== 'html') {
7662                   // When using a directive with replace:true and templateUrl the $compileNodes
7663                   // (or a child element inside of them)
7664                   // might change, so we need to recreate the namespace adapted compileNodes
7665                   // for call to the link function.
7666                   // Note: This will already clone the nodes...
7667                   $linkNode = jqLite(
7668                     wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7669                   );
7670                 } else if (cloneConnectFn) {
7671                   // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7672                   // and sometimes changes the structure of the DOM.
7673                   $linkNode = JQLitePrototype.clone.call($compileNodes);
7674                 } else {
7675                   $linkNode = $compileNodes;
7676                 }
7677
7678                 if (transcludeControllers) {
7679                   for (var controllerName in transcludeControllers) {
7680                     $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7681                   }
7682                 }
7683
7684                 compile.$$addScopeInfo($linkNode, scope);
7685
7686                 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7687                 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7688                 return $linkNode;
7689               };
7690             }
7691
7692             function detectNamespaceForChildElements(parentElement) {
7693               // TODO: Make this detect MathML as well...
7694               var node = parentElement && parentElement[0];
7695               if (!node) {
7696                 return 'html';
7697               } else {
7698                 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7699               }
7700             }
7701
7702             /**
7703              * Compile function matches each node in nodeList against the directives. Once all directives
7704              * for a particular node are collected their compile functions are executed. The compile
7705              * functions return values - the linking functions - are combined into a composite linking
7706              * function, which is the a linking function for the node.
7707              *
7708              * @param {NodeList} nodeList an array of nodes or NodeList to compile
7709              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7710              *        scope argument is auto-generated to the new child of the transcluded parent scope.
7711              * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7712              *        the rootElement must be set the jqLite collection of the compile root. This is
7713              *        needed so that the jqLite collection items can be replaced with widgets.
7714              * @param {number=} maxPriority Max directive priority.
7715              * @returns {Function} A composite linking function of all of the matched directives or null.
7716              */
7717             function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7718                                     previousCompileContext) {
7719               var linkFns = [],
7720                   attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7721
7722               for (var i = 0; i < nodeList.length; i++) {
7723                 attrs = new Attributes();
7724
7725                 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7726                 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7727                                                 ignoreDirective);
7728
7729                 nodeLinkFn = (directives.length)
7730                     ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7731                                               null, [], [], previousCompileContext)
7732                     : null;
7733
7734                 if (nodeLinkFn && nodeLinkFn.scope) {
7735                   compile.$$addScopeClass(attrs.$$element);
7736                 }
7737
7738                 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7739                               !(childNodes = nodeList[i].childNodes) ||
7740                               !childNodes.length)
7741                     ? null
7742                     : compileNodes(childNodes,
7743                          nodeLinkFn ? (
7744                           (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7745                              && nodeLinkFn.transclude) : transcludeFn);
7746
7747                 if (nodeLinkFn || childLinkFn) {
7748                   linkFns.push(i, nodeLinkFn, childLinkFn);
7749                   linkFnFound = true;
7750                   nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7751                 }
7752
7753                 //use the previous context only for the first element in the virtual group
7754                 previousCompileContext = null;
7755               }
7756
7757               // return a linking function if we have found anything, null otherwise
7758               return linkFnFound ? compositeLinkFn : null;
7759
7760               function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7761                 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7762                 var stableNodeList;
7763
7764
7765                 if (nodeLinkFnFound) {
7766                   // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7767                   // offsets don't get screwed up
7768                   var nodeListLength = nodeList.length;
7769                   stableNodeList = new Array(nodeListLength);
7770
7771                   // create a sparse array by only copying the elements which have a linkFn
7772                   for (i = 0; i < linkFns.length; i+=3) {
7773                     idx = linkFns[i];
7774                     stableNodeList[idx] = nodeList[idx];
7775                   }
7776                 } else {
7777                   stableNodeList = nodeList;
7778                 }
7779
7780                 for (i = 0, ii = linkFns.length; i < ii;) {
7781                   node = stableNodeList[linkFns[i++]];
7782                   nodeLinkFn = linkFns[i++];
7783                   childLinkFn = linkFns[i++];
7784
7785                   if (nodeLinkFn) {
7786                     if (nodeLinkFn.scope) {
7787                       childScope = scope.$new();
7788                       compile.$$addScopeInfo(jqLite(node), childScope);
7789                     } else {
7790                       childScope = scope;
7791                     }
7792
7793                     if (nodeLinkFn.transcludeOnThisElement) {
7794                       childBoundTranscludeFn = createBoundTranscludeFn(
7795                           scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7796
7797                     } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7798                       childBoundTranscludeFn = parentBoundTranscludeFn;
7799
7800                     } else if (!parentBoundTranscludeFn && transcludeFn) {
7801                       childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7802
7803                     } else {
7804                       childBoundTranscludeFn = null;
7805                     }
7806
7807                     nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7808
7809                   } else if (childLinkFn) {
7810                     childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7811                   }
7812                 }
7813               }
7814             }
7815
7816             function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7817
7818               var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7819
7820                 if (!transcludedScope) {
7821                   transcludedScope = scope.$new(false, containingScope);
7822                   transcludedScope.$$transcluded = true;
7823                 }
7824
7825                 return transcludeFn(transcludedScope, cloneFn, {
7826                   parentBoundTranscludeFn: previousBoundTranscludeFn,
7827                   transcludeControllers: controllers,
7828                   futureParentElement: futureParentElement
7829                 });
7830               };
7831
7832               return boundTranscludeFn;
7833             }
7834
7835             /**
7836              * Looks for directives on the given node and adds them to the directive collection which is
7837              * sorted.
7838              *
7839              * @param node Node to search.
7840              * @param directives An array to which the directives are added to. This array is sorted before
7841              *        the function returns.
7842              * @param attrs The shared attrs object which is used to populate the normalized attributes.
7843              * @param {number=} maxPriority Max directive priority.
7844              */
7845             function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7846               var nodeType = node.nodeType,
7847                   attrsMap = attrs.$attr,
7848                   match,
7849                   className;
7850
7851               switch (nodeType) {
7852                 case NODE_TYPE_ELEMENT: /* Element */
7853                   // use the node name: <directive>
7854                   addDirective(directives,
7855                       directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7856
7857                   // iterate over the attributes
7858                   for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7859                            j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7860                     var attrStartName = false;
7861                     var attrEndName = false;
7862
7863                     attr = nAttrs[j];
7864                     name = attr.name;
7865                     value = trim(attr.value);
7866
7867                     // support ngAttr attribute binding
7868                     ngAttrName = directiveNormalize(name);
7869                     if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7870                       name = name.replace(PREFIX_REGEXP, '')
7871                         .substr(8).replace(/_(.)/g, function(match, letter) {
7872                           return letter.toUpperCase();
7873                         });
7874                     }
7875
7876                     var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7877                     if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7878                       attrStartName = name;
7879                       attrEndName = name.substr(0, name.length - 5) + 'end';
7880                       name = name.substr(0, name.length - 6);
7881                     }
7882
7883                     nName = directiveNormalize(name.toLowerCase());
7884                     attrsMap[nName] = name;
7885                     if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7886                         attrs[nName] = value;
7887                         if (getBooleanAttrName(node, nName)) {
7888                           attrs[nName] = true; // presence means true
7889                         }
7890                     }
7891                     addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7892                     addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7893                                   attrEndName);
7894                   }
7895
7896                   // use class as directive
7897                   className = node.className;
7898                   if (isObject(className)) {
7899                       // Maybe SVGAnimatedString
7900                       className = className.animVal;
7901                   }
7902                   if (isString(className) && className !== '') {
7903                     while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7904                       nName = directiveNormalize(match[2]);
7905                       if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7906                         attrs[nName] = trim(match[3]);
7907                       }
7908                       className = className.substr(match.index + match[0].length);
7909                     }
7910                   }
7911                   break;
7912                 case NODE_TYPE_TEXT: /* Text Node */
7913                   if (msie === 11) {
7914                     // Workaround for #11781
7915                     while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7916                       node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7917                       node.parentNode.removeChild(node.nextSibling);
7918                     }
7919                   }
7920                   addTextInterpolateDirective(directives, node.nodeValue);
7921                   break;
7922                 case NODE_TYPE_COMMENT: /* Comment */
7923                   try {
7924                     match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7925                     if (match) {
7926                       nName = directiveNormalize(match[1]);
7927                       if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7928                         attrs[nName] = trim(match[2]);
7929                       }
7930                     }
7931                   } catch (e) {
7932                     // turns out that under some circumstances IE9 throws errors when one attempts to read
7933                     // comment's node value.
7934                     // Just ignore it and continue. (Can't seem to reproduce in test case.)
7935                   }
7936                   break;
7937               }
7938
7939               directives.sort(byPriority);
7940               return directives;
7941             }
7942
7943             /**
7944              * Given a node with an directive-start it collects all of the siblings until it finds
7945              * directive-end.
7946              * @param node
7947              * @param attrStart
7948              * @param attrEnd
7949              * @returns {*}
7950              */
7951             function groupScan(node, attrStart, attrEnd) {
7952               var nodes = [];
7953               var depth = 0;
7954               if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7955                 do {
7956                   if (!node) {
7957                     throw $compileMinErr('uterdir',
7958                               "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7959                               attrStart, attrEnd);
7960                   }
7961                   if (node.nodeType == NODE_TYPE_ELEMENT) {
7962                     if (node.hasAttribute(attrStart)) depth++;
7963                     if (node.hasAttribute(attrEnd)) depth--;
7964                   }
7965                   nodes.push(node);
7966                   node = node.nextSibling;
7967                 } while (depth > 0);
7968               } else {
7969                 nodes.push(node);
7970               }
7971
7972               return jqLite(nodes);
7973             }
7974
7975             /**
7976              * Wrapper for linking function which converts normal linking function into a grouped
7977              * linking function.
7978              * @param linkFn
7979              * @param attrStart
7980              * @param attrEnd
7981              * @returns {Function}
7982              */
7983             function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7984               return function(scope, element, attrs, controllers, transcludeFn) {
7985                 element = groupScan(element[0], attrStart, attrEnd);
7986                 return linkFn(scope, element, attrs, controllers, transcludeFn);
7987               };
7988             }
7989
7990             /**
7991              * Once the directives have been collected, their compile functions are executed. This method
7992              * is responsible for inlining directive templates as well as terminating the application
7993              * of the directives if the terminal directive has been reached.
7994              *
7995              * @param {Array} directives Array of collected directives to execute their compile function.
7996              *        this needs to be pre-sorted by priority order.
7997              * @param {Node} compileNode The raw DOM node to apply the compile functions to
7998              * @param {Object} templateAttrs The shared attribute function
7999              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8000              *                                                  scope argument is auto-generated to the new
8001              *                                                  child of the transcluded parent scope.
8002              * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8003              *                              argument has the root jqLite array so that we can replace nodes
8004              *                              on it.
8005              * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8006              *                                           compiling the transclusion.
8007              * @param {Array.<Function>} preLinkFns
8008              * @param {Array.<Function>} postLinkFns
8009              * @param {Object} previousCompileContext Context used for previous compilation of the current
8010              *                                        node
8011              * @returns {Function} linkFn
8012              */
8013             function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8014                                            jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8015                                            previousCompileContext) {
8016               previousCompileContext = previousCompileContext || {};
8017
8018               var terminalPriority = -Number.MAX_VALUE,
8019                   newScopeDirective = previousCompileContext.newScopeDirective,
8020                   controllerDirectives = previousCompileContext.controllerDirectives,
8021                   newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8022                   templateDirective = previousCompileContext.templateDirective,
8023                   nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8024                   hasTranscludeDirective = false,
8025                   hasTemplate = false,
8026                   hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8027                   $compileNode = templateAttrs.$$element = jqLite(compileNode),
8028                   directive,
8029                   directiveName,
8030                   $template,
8031                   replaceDirective = originalReplaceDirective,
8032                   childTranscludeFn = transcludeFn,
8033                   linkFn,
8034                   directiveValue;
8035
8036               // executes all directives on the current element
8037               for (var i = 0, ii = directives.length; i < ii; i++) {
8038                 directive = directives[i];
8039                 var attrStart = directive.$$start;
8040                 var attrEnd = directive.$$end;
8041
8042                 // collect multiblock sections
8043                 if (attrStart) {
8044                   $compileNode = groupScan(compileNode, attrStart, attrEnd);
8045                 }
8046                 $template = undefined;
8047
8048                 if (terminalPriority > directive.priority) {
8049                   break; // prevent further processing of directives
8050                 }
8051
8052                 if (directiveValue = directive.scope) {
8053
8054                   // skip the check for directives with async templates, we'll check the derived sync
8055                   // directive when the template arrives
8056                   if (!directive.templateUrl) {
8057                     if (isObject(directiveValue)) {
8058                       // This directive is trying to add an isolated scope.
8059                       // Check that there is no scope of any kind already
8060                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8061                                         directive, $compileNode);
8062                       newIsolateScopeDirective = directive;
8063                     } else {
8064                       // This directive is trying to add a child scope.
8065                       // Check that there is no isolated scope already
8066                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8067                                         $compileNode);
8068                     }
8069                   }
8070
8071                   newScopeDirective = newScopeDirective || directive;
8072                 }
8073
8074                 directiveName = directive.name;
8075
8076                 if (!directive.templateUrl && directive.controller) {
8077                   directiveValue = directive.controller;
8078                   controllerDirectives = controllerDirectives || createMap();
8079                   assertNoDuplicate("'" + directiveName + "' controller",
8080                       controllerDirectives[directiveName], directive, $compileNode);
8081                   controllerDirectives[directiveName] = directive;
8082                 }
8083
8084                 if (directiveValue = directive.transclude) {
8085                   hasTranscludeDirective = true;
8086
8087                   // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8088                   // This option should only be used by directives that know how to safely handle element transclusion,
8089                   // where the transcluded nodes are added or replaced after linking.
8090                   if (!directive.$$tlb) {
8091                     assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8092                     nonTlbTranscludeDirective = directive;
8093                   }
8094
8095                   if (directiveValue == 'element') {
8096                     hasElementTranscludeDirective = true;
8097                     terminalPriority = directive.priority;
8098                     $template = $compileNode;
8099                     $compileNode = templateAttrs.$$element =
8100                         jqLite(document.createComment(' ' + directiveName + ': ' +
8101                                                       templateAttrs[directiveName] + ' '));
8102                     compileNode = $compileNode[0];
8103                     replaceWith(jqCollection, sliceArgs($template), compileNode);
8104
8105                     childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8106                                                 replaceDirective && replaceDirective.name, {
8107                                                   // Don't pass in:
8108                                                   // - controllerDirectives - otherwise we'll create duplicates controllers
8109                                                   // - newIsolateScopeDirective or templateDirective - combining templates with
8110                                                   //   element transclusion doesn't make sense.
8111                                                   //
8112                                                   // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8113                                                   // on the same element more than once.
8114                                                   nonTlbTranscludeDirective: nonTlbTranscludeDirective
8115                                                 });
8116                   } else {
8117                     $template = jqLite(jqLiteClone(compileNode)).contents();
8118                     $compileNode.empty(); // clear contents
8119                     childTranscludeFn = compile($template, transcludeFn, undefined,
8120                         undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8121                   }
8122                 }
8123
8124                 if (directive.template) {
8125                   hasTemplate = true;
8126                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8127                   templateDirective = directive;
8128
8129                   directiveValue = (isFunction(directive.template))
8130                       ? directive.template($compileNode, templateAttrs)
8131                       : directive.template;
8132
8133                   directiveValue = denormalizeTemplate(directiveValue);
8134
8135                   if (directive.replace) {
8136                     replaceDirective = directive;
8137                     if (jqLiteIsTextNode(directiveValue)) {
8138                       $template = [];
8139                     } else {
8140                       $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8141                     }
8142                     compileNode = $template[0];
8143
8144                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8145                       throw $compileMinErr('tplrt',
8146                           "Template for directive '{0}' must have exactly one root element. {1}",
8147                           directiveName, '');
8148                     }
8149
8150                     replaceWith(jqCollection, $compileNode, compileNode);
8151
8152                     var newTemplateAttrs = {$attr: {}};
8153
8154                     // combine directives from the original node and from the template:
8155                     // - take the array of directives for this element
8156                     // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8157                     // - collect directives from the template and sort them by priority
8158                     // - combine directives as: processed + template + unprocessed
8159                     var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8160                     var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8161
8162                     if (newIsolateScopeDirective || newScopeDirective) {
8163                       // The original directive caused the current element to be replaced but this element
8164                       // also needs to have a new scope, so we need to tell the template directives
8165                       // that they would need to get their scope from further up, if they require transclusion
8166                       markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8167                     }
8168                     directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8169                     mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8170
8171                     ii = directives.length;
8172                   } else {
8173                     $compileNode.html(directiveValue);
8174                   }
8175                 }
8176
8177                 if (directive.templateUrl) {
8178                   hasTemplate = true;
8179                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8180                   templateDirective = directive;
8181
8182                   if (directive.replace) {
8183                     replaceDirective = directive;
8184                   }
8185
8186                   nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8187                       templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8188                         controllerDirectives: controllerDirectives,
8189                         newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8190                         newIsolateScopeDirective: newIsolateScopeDirective,
8191                         templateDirective: templateDirective,
8192                         nonTlbTranscludeDirective: nonTlbTranscludeDirective
8193                       });
8194                   ii = directives.length;
8195                 } else if (directive.compile) {
8196                   try {
8197                     linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8198                     if (isFunction(linkFn)) {
8199                       addLinkFns(null, linkFn, attrStart, attrEnd);
8200                     } else if (linkFn) {
8201                       addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8202                     }
8203                   } catch (e) {
8204                     $exceptionHandler(e, startingTag($compileNode));
8205                   }
8206                 }
8207
8208                 if (directive.terminal) {
8209                   nodeLinkFn.terminal = true;
8210                   terminalPriority = Math.max(terminalPriority, directive.priority);
8211                 }
8212
8213               }
8214
8215               nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8216               nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8217               nodeLinkFn.templateOnThisElement = hasTemplate;
8218               nodeLinkFn.transclude = childTranscludeFn;
8219
8220               previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8221
8222               // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8223               return nodeLinkFn;
8224
8225               ////////////////////
8226
8227               function addLinkFns(pre, post, attrStart, attrEnd) {
8228                 if (pre) {
8229                   if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8230                   pre.require = directive.require;
8231                   pre.directiveName = directiveName;
8232                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8233                     pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8234                   }
8235                   preLinkFns.push(pre);
8236                 }
8237                 if (post) {
8238                   if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8239                   post.require = directive.require;
8240                   post.directiveName = directiveName;
8241                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8242                     post = cloneAndAnnotateFn(post, {isolateScope: true});
8243                   }
8244                   postLinkFns.push(post);
8245                 }
8246               }
8247
8248
8249               function getControllers(directiveName, require, $element, elementControllers) {
8250                 var value;
8251
8252                 if (isString(require)) {
8253                   var match = require.match(REQUIRE_PREFIX_REGEXP);
8254                   var name = require.substring(match[0].length);
8255                   var inheritType = match[1] || match[3];
8256                   var optional = match[2] === '?';
8257
8258                   //If only parents then start at the parent element
8259                   if (inheritType === '^^') {
8260                     $element = $element.parent();
8261                   //Otherwise attempt getting the controller from elementControllers in case
8262                   //the element is transcluded (and has no data) and to avoid .data if possible
8263                   } else {
8264                     value = elementControllers && elementControllers[name];
8265                     value = value && value.instance;
8266                   }
8267
8268                   if (!value) {
8269                     var dataName = '$' + name + 'Controller';
8270                     value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8271                   }
8272
8273                   if (!value && !optional) {
8274                     throw $compileMinErr('ctreq',
8275                         "Controller '{0}', required by directive '{1}', can't be found!",
8276                         name, directiveName);
8277                   }
8278                 } else if (isArray(require)) {
8279                   value = [];
8280                   for (var i = 0, ii = require.length; i < ii; i++) {
8281                     value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8282                   }
8283                 }
8284
8285                 return value || null;
8286               }
8287
8288               function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8289                 var elementControllers = createMap();
8290                 for (var controllerKey in controllerDirectives) {
8291                   var directive = controllerDirectives[controllerKey];
8292                   var locals = {
8293                     $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8294                     $element: $element,
8295                     $attrs: attrs,
8296                     $transclude: transcludeFn
8297                   };
8298
8299                   var controller = directive.controller;
8300                   if (controller == '@') {
8301                     controller = attrs[directive.name];
8302                   }
8303
8304                   var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8305
8306                   // For directives with element transclusion the element is a comment,
8307                   // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8308                   // clean up (http://bugs.jquery.com/ticket/8335).
8309                   // Instead, we save the controllers for the element in a local hash and attach to .data
8310                   // later, once we have the actual element.
8311                   elementControllers[directive.name] = controllerInstance;
8312                   if (!hasElementTranscludeDirective) {
8313                     $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8314                   }
8315                 }
8316                 return elementControllers;
8317               }
8318
8319               function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8320                 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8321                     attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8322
8323                 if (compileNode === linkNode) {
8324                   attrs = templateAttrs;
8325                   $element = templateAttrs.$$element;
8326                 } else {
8327                   $element = jqLite(linkNode);
8328                   attrs = new Attributes($element, templateAttrs);
8329                 }
8330
8331                 controllerScope = scope;
8332                 if (newIsolateScopeDirective) {
8333                   isolateScope = scope.$new(true);
8334                 } else if (newScopeDirective) {
8335                   controllerScope = scope.$parent;
8336                 }
8337
8338                 if (boundTranscludeFn) {
8339                   // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8340                   // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8341                   transcludeFn = controllersBoundTransclude;
8342                   transcludeFn.$$boundTransclude = boundTranscludeFn;
8343                 }
8344
8345                 if (controllerDirectives) {
8346                   elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8347                 }
8348
8349                 if (newIsolateScopeDirective) {
8350                   // Initialize isolate scope bindings for new isolate scope directive.
8351                   compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8352                       templateDirective === newIsolateScopeDirective.$$originalDirective)));
8353                   compile.$$addScopeClass($element, true);
8354                   isolateScope.$$isolateBindings =
8355                       newIsolateScopeDirective.$$isolateBindings;
8356                   removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8357                                                 isolateScope.$$isolateBindings,
8358                                                 newIsolateScopeDirective);
8359                   if (removeScopeBindingWatches) {
8360                     isolateScope.$on('$destroy', removeScopeBindingWatches);
8361                   }
8362                 }
8363
8364                 // Initialize bindToController bindings
8365                 for (var name in elementControllers) {
8366                   var controllerDirective = controllerDirectives[name];
8367                   var controller = elementControllers[name];
8368                   var bindings = controllerDirective.$$bindings.bindToController;
8369
8370                   if (controller.identifier && bindings) {
8371                     removeControllerBindingWatches =
8372                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8373                   }
8374
8375                   var controllerResult = controller();
8376                   if (controllerResult !== controller.instance) {
8377                     // If the controller constructor has a return value, overwrite the instance
8378                     // from setupControllers
8379                     controller.instance = controllerResult;
8380                     $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8381                     removeControllerBindingWatches && removeControllerBindingWatches();
8382                     removeControllerBindingWatches =
8383                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8384                   }
8385                 }
8386
8387                 // PRELINKING
8388                 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8389                   linkFn = preLinkFns[i];
8390                   invokeLinkFn(linkFn,
8391                       linkFn.isolateScope ? isolateScope : scope,
8392                       $element,
8393                       attrs,
8394                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8395                       transcludeFn
8396                   );
8397                 }
8398
8399                 // RECURSION
8400                 // We only pass the isolate scope, if the isolate directive has a template,
8401                 // otherwise the child elements do not belong to the isolate directive.
8402                 var scopeToChild = scope;
8403                 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8404                   scopeToChild = isolateScope;
8405                 }
8406                 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8407
8408                 // POSTLINKING
8409                 for (i = postLinkFns.length - 1; i >= 0; i--) {
8410                   linkFn = postLinkFns[i];
8411                   invokeLinkFn(linkFn,
8412                       linkFn.isolateScope ? isolateScope : scope,
8413                       $element,
8414                       attrs,
8415                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8416                       transcludeFn
8417                   );
8418                 }
8419
8420                 // This is the function that is injected as `$transclude`.
8421                 // Note: all arguments are optional!
8422                 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8423                   var transcludeControllers;
8424
8425                   // No scope passed in:
8426                   if (!isScope(scope)) {
8427                     futureParentElement = cloneAttachFn;
8428                     cloneAttachFn = scope;
8429                     scope = undefined;
8430                   }
8431
8432                   if (hasElementTranscludeDirective) {
8433                     transcludeControllers = elementControllers;
8434                   }
8435                   if (!futureParentElement) {
8436                     futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8437                   }
8438                   return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8439                 }
8440               }
8441             }
8442
8443             // Depending upon the context in which a directive finds itself it might need to have a new isolated
8444             // or child scope created. For instance:
8445             // * if the directive has been pulled into a template because another directive with a higher priority
8446             // asked for element transclusion
8447             // * if the directive itself asks for transclusion but it is at the root of a template and the original
8448             // element was replaced. See https://github.com/angular/angular.js/issues/12936
8449             function markDirectiveScope(directives, isolateScope, newScope) {
8450               for (var j = 0, jj = directives.length; j < jj; j++) {
8451                 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8452               }
8453             }
8454
8455             /**
8456              * looks up the directive and decorates it with exception handling and proper parameters. We
8457              * call this the boundDirective.
8458              *
8459              * @param {string} name name of the directive to look up.
8460              * @param {string} location The directive must be found in specific format.
8461              *   String containing any of theses characters:
8462              *
8463              *   * `E`: element name
8464              *   * `A': attribute
8465              *   * `C`: class
8466              *   * `M`: comment
8467              * @returns {boolean} true if directive was added.
8468              */
8469             function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8470                                   endAttrName) {
8471               if (name === ignoreDirective) return null;
8472               var match = null;
8473               if (hasDirectives.hasOwnProperty(name)) {
8474                 for (var directive, directives = $injector.get(name + Suffix),
8475                     i = 0, ii = directives.length; i < ii; i++) {
8476                   try {
8477                     directive = directives[i];
8478                     if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8479                          directive.restrict.indexOf(location) != -1) {
8480                       if (startAttrName) {
8481                         directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8482                       }
8483                       tDirectives.push(directive);
8484                       match = directive;
8485                     }
8486                   } catch (e) { $exceptionHandler(e); }
8487                 }
8488               }
8489               return match;
8490             }
8491
8492
8493             /**
8494              * looks up the directive and returns true if it is a multi-element directive,
8495              * and therefore requires DOM nodes between -start and -end markers to be grouped
8496              * together.
8497              *
8498              * @param {string} name name of the directive to look up.
8499              * @returns true if directive was registered as multi-element.
8500              */
8501             function directiveIsMultiElement(name) {
8502               if (hasDirectives.hasOwnProperty(name)) {
8503                 for (var directive, directives = $injector.get(name + Suffix),
8504                     i = 0, ii = directives.length; i < ii; i++) {
8505                   directive = directives[i];
8506                   if (directive.multiElement) {
8507                     return true;
8508                   }
8509                 }
8510               }
8511               return false;
8512             }
8513
8514             /**
8515              * When the element is replaced with HTML template then the new attributes
8516              * on the template need to be merged with the existing attributes in the DOM.
8517              * The desired effect is to have both of the attributes present.
8518              *
8519              * @param {object} dst destination attributes (original DOM)
8520              * @param {object} src source attributes (from the directive template)
8521              */
8522             function mergeTemplateAttributes(dst, src) {
8523               var srcAttr = src.$attr,
8524                   dstAttr = dst.$attr,
8525                   $element = dst.$$element;
8526
8527               // reapply the old attributes to the new element
8528               forEach(dst, function(value, key) {
8529                 if (key.charAt(0) != '$') {
8530                   if (src[key] && src[key] !== value) {
8531                     value += (key === 'style' ? ';' : ' ') + src[key];
8532                   }
8533                   dst.$set(key, value, true, srcAttr[key]);
8534                 }
8535               });
8536
8537               // copy the new attributes on the old attrs object
8538               forEach(src, function(value, key) {
8539                 if (key == 'class') {
8540                   safeAddClass($element, value);
8541                   dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8542                 } else if (key == 'style') {
8543                   $element.attr('style', $element.attr('style') + ';' + value);
8544                   dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8545                   // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8546                   // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8547                   // have an attribute like "has-own-property" or "data-has-own-property", etc.
8548                 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8549                   dst[key] = value;
8550                   dstAttr[key] = srcAttr[key];
8551                 }
8552               });
8553             }
8554
8555
8556             function compileTemplateUrl(directives, $compileNode, tAttrs,
8557                 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8558               var linkQueue = [],
8559                   afterTemplateNodeLinkFn,
8560                   afterTemplateChildLinkFn,
8561                   beforeTemplateCompileNode = $compileNode[0],
8562                   origAsyncDirective = directives.shift(),
8563                   derivedSyncDirective = inherit(origAsyncDirective, {
8564                     templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8565                   }),
8566                   templateUrl = (isFunction(origAsyncDirective.templateUrl))
8567                       ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8568                       : origAsyncDirective.templateUrl,
8569                   templateNamespace = origAsyncDirective.templateNamespace;
8570
8571               $compileNode.empty();
8572
8573               $templateRequest(templateUrl)
8574                 .then(function(content) {
8575                   var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8576
8577                   content = denormalizeTemplate(content);
8578
8579                   if (origAsyncDirective.replace) {
8580                     if (jqLiteIsTextNode(content)) {
8581                       $template = [];
8582                     } else {
8583                       $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8584                     }
8585                     compileNode = $template[0];
8586
8587                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8588                       throw $compileMinErr('tplrt',
8589                           "Template for directive '{0}' must have exactly one root element. {1}",
8590                           origAsyncDirective.name, templateUrl);
8591                     }
8592
8593                     tempTemplateAttrs = {$attr: {}};
8594                     replaceWith($rootElement, $compileNode, compileNode);
8595                     var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8596
8597                     if (isObject(origAsyncDirective.scope)) {
8598                       // the original directive that caused the template to be loaded async required
8599                       // an isolate scope
8600                       markDirectiveScope(templateDirectives, true);
8601                     }
8602                     directives = templateDirectives.concat(directives);
8603                     mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8604                   } else {
8605                     compileNode = beforeTemplateCompileNode;
8606                     $compileNode.html(content);
8607                   }
8608
8609                   directives.unshift(derivedSyncDirective);
8610
8611                   afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8612                       childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8613                       previousCompileContext);
8614                   forEach($rootElement, function(node, i) {
8615                     if (node == compileNode) {
8616                       $rootElement[i] = $compileNode[0];
8617                     }
8618                   });
8619                   afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8620
8621                   while (linkQueue.length) {
8622                     var scope = linkQueue.shift(),
8623                         beforeTemplateLinkNode = linkQueue.shift(),
8624                         linkRootElement = linkQueue.shift(),
8625                         boundTranscludeFn = linkQueue.shift(),
8626                         linkNode = $compileNode[0];
8627
8628                     if (scope.$$destroyed) continue;
8629
8630                     if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8631                       var oldClasses = beforeTemplateLinkNode.className;
8632
8633                       if (!(previousCompileContext.hasElementTranscludeDirective &&
8634                           origAsyncDirective.replace)) {
8635                         // it was cloned therefore we have to clone as well.
8636                         linkNode = jqLiteClone(compileNode);
8637                       }
8638                       replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8639
8640                       // Copy in CSS classes from original node
8641                       safeAddClass(jqLite(linkNode), oldClasses);
8642                     }
8643                     if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8644                       childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8645                     } else {
8646                       childBoundTranscludeFn = boundTranscludeFn;
8647                     }
8648                     afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8649                       childBoundTranscludeFn);
8650                   }
8651                   linkQueue = null;
8652                 });
8653
8654               return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8655                 var childBoundTranscludeFn = boundTranscludeFn;
8656                 if (scope.$$destroyed) return;
8657                 if (linkQueue) {
8658                   linkQueue.push(scope,
8659                                  node,
8660                                  rootElement,
8661                                  childBoundTranscludeFn);
8662                 } else {
8663                   if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8664                     childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8665                   }
8666                   afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8667                 }
8668               };
8669             }
8670
8671
8672             /**
8673              * Sorting function for bound directives.
8674              */
8675             function byPriority(a, b) {
8676               var diff = b.priority - a.priority;
8677               if (diff !== 0) return diff;
8678               if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8679               return a.index - b.index;
8680             }
8681
8682             function assertNoDuplicate(what, previousDirective, directive, element) {
8683
8684               function wrapModuleNameIfDefined(moduleName) {
8685                 return moduleName ?
8686                   (' (module: ' + moduleName + ')') :
8687                   '';
8688               }
8689
8690               if (previousDirective) {
8691                 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8692                     previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8693                     directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8694               }
8695             }
8696
8697
8698             function addTextInterpolateDirective(directives, text) {
8699               var interpolateFn = $interpolate(text, true);
8700               if (interpolateFn) {
8701                 directives.push({
8702                   priority: 0,
8703                   compile: function textInterpolateCompileFn(templateNode) {
8704                     var templateNodeParent = templateNode.parent(),
8705                         hasCompileParent = !!templateNodeParent.length;
8706
8707                     // When transcluding a template that has bindings in the root
8708                     // we don't have a parent and thus need to add the class during linking fn.
8709                     if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8710
8711                     return function textInterpolateLinkFn(scope, node) {
8712                       var parent = node.parent();
8713                       if (!hasCompileParent) compile.$$addBindingClass(parent);
8714                       compile.$$addBindingInfo(parent, interpolateFn.expressions);
8715                       scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8716                         node[0].nodeValue = value;
8717                       });
8718                     };
8719                   }
8720                 });
8721               }
8722             }
8723
8724
8725             function wrapTemplate(type, template) {
8726               type = lowercase(type || 'html');
8727               switch (type) {
8728               case 'svg':
8729               case 'math':
8730                 var wrapper = document.createElement('div');
8731                 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8732                 return wrapper.childNodes[0].childNodes;
8733               default:
8734                 return template;
8735               }
8736             }
8737
8738
8739             function getTrustedContext(node, attrNormalizedName) {
8740               if (attrNormalizedName == "srcdoc") {
8741                 return $sce.HTML;
8742               }
8743               var tag = nodeName_(node);
8744               // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8745               if (attrNormalizedName == "xlinkHref" ||
8746                   (tag == "form" && attrNormalizedName == "action") ||
8747                   (tag != "img" && (attrNormalizedName == "src" ||
8748                                     attrNormalizedName == "ngSrc"))) {
8749                 return $sce.RESOURCE_URL;
8750               }
8751             }
8752
8753
8754             function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8755               var trustedContext = getTrustedContext(node, name);
8756               allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8757
8758               var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8759
8760               // no interpolation found -> ignore
8761               if (!interpolateFn) return;
8762
8763
8764               if (name === "multiple" && nodeName_(node) === "select") {
8765                 throw $compileMinErr("selmulti",
8766                     "Binding to the 'multiple' attribute is not supported. Element: {0}",
8767                     startingTag(node));
8768               }
8769
8770               directives.push({
8771                 priority: 100,
8772                 compile: function() {
8773                     return {
8774                       pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8775                         var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8776
8777                         if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8778                           throw $compileMinErr('nodomevents',
8779                               "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8780                                   "ng- versions (such as ng-click instead of onclick) instead.");
8781                         }
8782
8783                         // If the attribute has changed since last $interpolate()ed
8784                         var newValue = attr[name];
8785                         if (newValue !== value) {
8786                           // we need to interpolate again since the attribute value has been updated
8787                           // (e.g. by another directive's compile function)
8788                           // ensure unset/empty values make interpolateFn falsy
8789                           interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8790                           value = newValue;
8791                         }
8792
8793                         // if attribute was updated so that there is no interpolation going on we don't want to
8794                         // register any observers
8795                         if (!interpolateFn) return;
8796
8797                         // initialize attr object so that it's ready in case we need the value for isolate
8798                         // scope initialization, otherwise the value would not be available from isolate
8799                         // directive's linking fn during linking phase
8800                         attr[name] = interpolateFn(scope);
8801
8802                         ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8803                         (attr.$$observers && attr.$$observers[name].$$scope || scope).
8804                           $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8805                             //special case for class attribute addition + removal
8806                             //so that class changes can tap into the animation
8807                             //hooks provided by the $animate service. Be sure to
8808                             //skip animations when the first digest occurs (when
8809                             //both the new and the old values are the same) since
8810                             //the CSS classes are the non-interpolated values
8811                             if (name === 'class' && newValue != oldValue) {
8812                               attr.$updateClass(newValue, oldValue);
8813                             } else {
8814                               attr.$set(name, newValue);
8815                             }
8816                           });
8817                       }
8818                     };
8819                   }
8820               });
8821             }
8822
8823
8824             /**
8825              * This is a special jqLite.replaceWith, which can replace items which
8826              * have no parents, provided that the containing jqLite collection is provided.
8827              *
8828              * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8829              *                               in the root of the tree.
8830              * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8831              *                                  the shell, but replace its DOM node reference.
8832              * @param {Node} newNode The new DOM node.
8833              */
8834             function replaceWith($rootElement, elementsToRemove, newNode) {
8835               var firstElementToRemove = elementsToRemove[0],
8836                   removeCount = elementsToRemove.length,
8837                   parent = firstElementToRemove.parentNode,
8838                   i, ii;
8839
8840               if ($rootElement) {
8841                 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8842                   if ($rootElement[i] == firstElementToRemove) {
8843                     $rootElement[i++] = newNode;
8844                     for (var j = i, j2 = j + removeCount - 1,
8845                              jj = $rootElement.length;
8846                          j < jj; j++, j2++) {
8847                       if (j2 < jj) {
8848                         $rootElement[j] = $rootElement[j2];
8849                       } else {
8850                         delete $rootElement[j];
8851                       }
8852                     }
8853                     $rootElement.length -= removeCount - 1;
8854
8855                     // If the replaced element is also the jQuery .context then replace it
8856                     // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8857                     // http://api.jquery.com/context/
8858                     if ($rootElement.context === firstElementToRemove) {
8859                       $rootElement.context = newNode;
8860                     }
8861                     break;
8862                   }
8863                 }
8864               }
8865
8866               if (parent) {
8867                 parent.replaceChild(newNode, firstElementToRemove);
8868               }
8869
8870               // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8871               var fragment = document.createDocumentFragment();
8872               fragment.appendChild(firstElementToRemove);
8873
8874               if (jqLite.hasData(firstElementToRemove)) {
8875                 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8876                 // data here because there's no public interface in jQuery to do that and copying over
8877                 // event listeners (which is the main use of private data) wouldn't work anyway.
8878                 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8879
8880                 // Remove data of the replaced element. We cannot just call .remove()
8881                 // on the element it since that would deallocate scope that is needed
8882                 // for the new node. Instead, remove the data "manually".
8883                 if (!jQuery) {
8884                   delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8885                 } else {
8886                   // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8887                   // the replaced element. The cleanData version monkey-patched by Angular would cause
8888                   // the scope to be trashed and we do need the very same scope to work with the new
8889                   // element. However, we cannot just cache the non-patched version and use it here as
8890                   // that would break if another library patches the method after Angular does (one
8891                   // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8892                   // skipped this one time.
8893                   skipDestroyOnNextJQueryCleanData = true;
8894                   jQuery.cleanData([firstElementToRemove]);
8895                 }
8896               }
8897
8898               for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8899                 var element = elementsToRemove[k];
8900                 jqLite(element).remove(); // must do this way to clean up expando
8901                 fragment.appendChild(element);
8902                 delete elementsToRemove[k];
8903               }
8904
8905               elementsToRemove[0] = newNode;
8906               elementsToRemove.length = 1;
8907             }
8908
8909
8910             function cloneAndAnnotateFn(fn, annotation) {
8911               return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8912             }
8913
8914
8915             function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8916               try {
8917                 linkFn(scope, $element, attrs, controllers, transcludeFn);
8918               } catch (e) {
8919                 $exceptionHandler(e, startingTag($element));
8920               }
8921             }
8922
8923
8924             // Set up $watches for isolate scope and controller bindings. This process
8925             // only occurs for isolate scopes and new scopes with controllerAs.
8926             function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8927               var removeWatchCollection = [];
8928               forEach(bindings, function(definition, scopeName) {
8929                 var attrName = definition.attrName,
8930                 optional = definition.optional,
8931                 mode = definition.mode, // @, =, or &
8932                 lastValue,
8933                 parentGet, parentSet, compare;
8934
8935                 switch (mode) {
8936
8937                   case '@':
8938                     if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8939                       destination[scopeName] = attrs[attrName] = void 0;
8940                     }
8941                     attrs.$observe(attrName, function(value) {
8942                       if (isString(value)) {
8943                         destination[scopeName] = value;
8944                       }
8945                     });
8946                     attrs.$$observers[attrName].$$scope = scope;
8947                     if (isString(attrs[attrName])) {
8948                       // If the attribute has been provided then we trigger an interpolation to ensure
8949                       // the value is there for use in the link fn
8950                       destination[scopeName] = $interpolate(attrs[attrName])(scope);
8951                     }
8952                     break;
8953
8954                   case '=':
8955                     if (!hasOwnProperty.call(attrs, attrName)) {
8956                       if (optional) break;
8957                       attrs[attrName] = void 0;
8958                     }
8959                     if (optional && !attrs[attrName]) break;
8960
8961                     parentGet = $parse(attrs[attrName]);
8962                     if (parentGet.literal) {
8963                       compare = equals;
8964                     } else {
8965                       compare = function(a, b) { return a === b || (a !== a && b !== b); };
8966                     }
8967                     parentSet = parentGet.assign || function() {
8968                       // reset the change, or we will throw this exception on every $digest
8969                       lastValue = destination[scopeName] = parentGet(scope);
8970                       throw $compileMinErr('nonassign',
8971                           "Expression '{0}' used with directive '{1}' is non-assignable!",
8972                           attrs[attrName], directive.name);
8973                     };
8974                     lastValue = destination[scopeName] = parentGet(scope);
8975                     var parentValueWatch = function parentValueWatch(parentValue) {
8976                       if (!compare(parentValue, destination[scopeName])) {
8977                         // we are out of sync and need to copy
8978                         if (!compare(parentValue, lastValue)) {
8979                           // parent changed and it has precedence
8980                           destination[scopeName] = parentValue;
8981                         } else {
8982                           // if the parent can be assigned then do so
8983                           parentSet(scope, parentValue = destination[scopeName]);
8984                         }
8985                       }
8986                       return lastValue = parentValue;
8987                     };
8988                     parentValueWatch.$stateful = true;
8989                     var removeWatch;
8990                     if (definition.collection) {
8991                       removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8992                     } else {
8993                       removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8994                     }
8995                     removeWatchCollection.push(removeWatch);
8996                     break;
8997
8998                   case '&':
8999                     // Don't assign Object.prototype method to scope
9000                     parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9001
9002                     // Don't assign noop to destination if expression is not valid
9003                     if (parentGet === noop && optional) break;
9004
9005                     destination[scopeName] = function(locals) {
9006                       return parentGet(scope, locals);
9007                     };
9008                     break;
9009                 }
9010               });
9011
9012               return removeWatchCollection.length && function removeWatches() {
9013                 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9014                   removeWatchCollection[i]();
9015                 }
9016               };
9017             }
9018           }];
9019         }
9020
9021         var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9022         /**
9023          * Converts all accepted directives format into proper directive name.
9024          * @param name Name to normalize
9025          */
9026         function directiveNormalize(name) {
9027           return camelCase(name.replace(PREFIX_REGEXP, ''));
9028         }
9029
9030         /**
9031          * @ngdoc type
9032          * @name $compile.directive.Attributes
9033          *
9034          * @description
9035          * A shared object between directive compile / linking functions which contains normalized DOM
9036          * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9037          * needed since all of these are treated as equivalent in Angular:
9038          *
9039          * ```
9040          *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9041          * ```
9042          */
9043
9044         /**
9045          * @ngdoc property
9046          * @name $compile.directive.Attributes#$attr
9047          *
9048          * @description
9049          * A map of DOM element attribute names to the normalized name. This is
9050          * needed to do reverse lookup from normalized name back to actual name.
9051          */
9052
9053
9054         /**
9055          * @ngdoc method
9056          * @name $compile.directive.Attributes#$set
9057          * @kind function
9058          *
9059          * @description
9060          * Set DOM element attribute value.
9061          *
9062          *
9063          * @param {string} name Normalized element attribute name of the property to modify. The name is
9064          *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9065          *          property to the original name.
9066          * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9067          */
9068
9069
9070
9071         /**
9072          * Closure compiler type information
9073          */
9074
9075         function nodesetLinkingFn(
9076           /* angular.Scope */ scope,
9077           /* NodeList */ nodeList,
9078           /* Element */ rootElement,
9079           /* function(Function) */ boundTranscludeFn
9080         ) {}
9081
9082         function directiveLinkingFn(
9083           /* nodesetLinkingFn */ nodesetLinkingFn,
9084           /* angular.Scope */ scope,
9085           /* Node */ node,
9086           /* Element */ rootElement,
9087           /* function(Function) */ boundTranscludeFn
9088         ) {}
9089
9090         function tokenDifference(str1, str2) {
9091           var values = '',
9092               tokens1 = str1.split(/\s+/),
9093               tokens2 = str2.split(/\s+/);
9094
9095           outer:
9096           for (var i = 0; i < tokens1.length; i++) {
9097             var token = tokens1[i];
9098             for (var j = 0; j < tokens2.length; j++) {
9099               if (token == tokens2[j]) continue outer;
9100             }
9101             values += (values.length > 0 ? ' ' : '') + token;
9102           }
9103           return values;
9104         }
9105
9106         function removeComments(jqNodes) {
9107           jqNodes = jqLite(jqNodes);
9108           var i = jqNodes.length;
9109
9110           if (i <= 1) {
9111             return jqNodes;
9112           }
9113
9114           while (i--) {
9115             var node = jqNodes[i];
9116             if (node.nodeType === NODE_TYPE_COMMENT) {
9117               splice.call(jqNodes, i, 1);
9118             }
9119           }
9120           return jqNodes;
9121         }
9122
9123         var $controllerMinErr = minErr('$controller');
9124
9125
9126         var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9127         function identifierForController(controller, ident) {
9128           if (ident && isString(ident)) return ident;
9129           if (isString(controller)) {
9130             var match = CNTRL_REG.exec(controller);
9131             if (match) return match[3];
9132           }
9133         }
9134
9135
9136         /**
9137          * @ngdoc provider
9138          * @name $controllerProvider
9139          * @description
9140          * The {@link ng.$controller $controller service} is used by Angular to create new
9141          * controllers.
9142          *
9143          * This provider allows controller registration via the
9144          * {@link ng.$controllerProvider#register register} method.
9145          */
9146         function $ControllerProvider() {
9147           var controllers = {},
9148               globals = false;
9149
9150           /**
9151            * @ngdoc method
9152            * @name $controllerProvider#register
9153            * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9154            *    the names and the values are the constructors.
9155            * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9156            *    annotations in the array notation).
9157            */
9158           this.register = function(name, constructor) {
9159             assertNotHasOwnProperty(name, 'controller');
9160             if (isObject(name)) {
9161               extend(controllers, name);
9162             } else {
9163               controllers[name] = constructor;
9164             }
9165           };
9166
9167           /**
9168            * @ngdoc method
9169            * @name $controllerProvider#allowGlobals
9170            * @description If called, allows `$controller` to find controller constructors on `window`
9171            */
9172           this.allowGlobals = function() {
9173             globals = true;
9174           };
9175
9176
9177           this.$get = ['$injector', '$window', function($injector, $window) {
9178
9179             /**
9180              * @ngdoc service
9181              * @name $controller
9182              * @requires $injector
9183              *
9184              * @param {Function|string} constructor If called with a function then it's considered to be the
9185              *    controller constructor function. Otherwise it's considered to be a string which is used
9186              *    to retrieve the controller constructor using the following steps:
9187              *
9188              *    * check if a controller with given name is registered via `$controllerProvider`
9189              *    * check if evaluating the string on the current scope returns a constructor
9190              *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9191              *      `window` object (not recommended)
9192              *
9193              *    The string can use the `controller as property` syntax, where the controller instance is published
9194              *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9195              *    to work correctly.
9196              *
9197              * @param {Object} locals Injection locals for Controller.
9198              * @return {Object} Instance of given controller.
9199              *
9200              * @description
9201              * `$controller` service is responsible for instantiating controllers.
9202              *
9203              * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9204              * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9205              */
9206             return function(expression, locals, later, ident) {
9207               // PRIVATE API:
9208               //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9209               //                     If true, $controller will allocate the object with the correct
9210               //                     prototype chain, but will not invoke the controller until a returned
9211               //                     callback is invoked.
9212               //   param `ident` --- An optional label which overrides the label parsed from the controller
9213               //                     expression, if any.
9214               var instance, match, constructor, identifier;
9215               later = later === true;
9216               if (ident && isString(ident)) {
9217                 identifier = ident;
9218               }
9219
9220               if (isString(expression)) {
9221                 match = expression.match(CNTRL_REG);
9222                 if (!match) {
9223                   throw $controllerMinErr('ctrlfmt',
9224                     "Badly formed controller string '{0}'. " +
9225                     "Must match `__name__ as __id__` or `__name__`.", expression);
9226                 }
9227                 constructor = match[1],
9228                 identifier = identifier || match[3];
9229                 expression = controllers.hasOwnProperty(constructor)
9230                     ? controllers[constructor]
9231                     : getter(locals.$scope, constructor, true) ||
9232                         (globals ? getter($window, constructor, true) : undefined);
9233
9234                 assertArgFn(expression, constructor, true);
9235               }
9236
9237               if (later) {
9238                 // Instantiate controller later:
9239                 // This machinery is used to create an instance of the object before calling the
9240                 // controller's constructor itself.
9241                 //
9242                 // This allows properties to be added to the controller before the constructor is
9243                 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9244                 //
9245                 // This feature is not intended for use by applications, and is thus not documented
9246                 // publicly.
9247                 // Object creation: http://jsperf.com/create-constructor/2
9248                 var controllerPrototype = (isArray(expression) ?
9249                   expression[expression.length - 1] : expression).prototype;
9250                 instance = Object.create(controllerPrototype || null);
9251
9252                 if (identifier) {
9253                   addIdentifier(locals, identifier, instance, constructor || expression.name);
9254                 }
9255
9256                 var instantiate;
9257                 return instantiate = extend(function() {
9258                   var result = $injector.invoke(expression, instance, locals, constructor);
9259                   if (result !== instance && (isObject(result) || isFunction(result))) {
9260                     instance = result;
9261                     if (identifier) {
9262                       // If result changed, re-assign controllerAs value to scope.
9263                       addIdentifier(locals, identifier, instance, constructor || expression.name);
9264                     }
9265                   }
9266                   return instance;
9267                 }, {
9268                   instance: instance,
9269                   identifier: identifier
9270                 });
9271               }
9272
9273               instance = $injector.instantiate(expression, locals, constructor);
9274
9275               if (identifier) {
9276                 addIdentifier(locals, identifier, instance, constructor || expression.name);
9277               }
9278
9279               return instance;
9280             };
9281
9282             function addIdentifier(locals, identifier, instance, name) {
9283               if (!(locals && isObject(locals.$scope))) {
9284                 throw minErr('$controller')('noscp',
9285                   "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9286                   name, identifier);
9287               }
9288
9289               locals.$scope[identifier] = instance;
9290             }
9291           }];
9292         }
9293
9294         /**
9295          * @ngdoc service
9296          * @name $document
9297          * @requires $window
9298          *
9299          * @description
9300          * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9301          *
9302          * @example
9303            <example module="documentExample">
9304              <file name="index.html">
9305                <div ng-controller="ExampleController">
9306                  <p>$document title: <b ng-bind="title"></b></p>
9307                  <p>window.document title: <b ng-bind="windowTitle"></b></p>
9308                </div>
9309              </file>
9310              <file name="script.js">
9311                angular.module('documentExample', [])
9312                  .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9313                    $scope.title = $document[0].title;
9314                    $scope.windowTitle = angular.element(window.document)[0].title;
9315                  }]);
9316              </file>
9317            </example>
9318          */
9319         function $DocumentProvider() {
9320           this.$get = ['$window', function(window) {
9321             return jqLite(window.document);
9322           }];
9323         }
9324
9325         /**
9326          * @ngdoc service
9327          * @name $exceptionHandler
9328          * @requires ng.$log
9329          *
9330          * @description
9331          * Any uncaught exception in angular expressions is delegated to this service.
9332          * The default implementation simply delegates to `$log.error` which logs it into
9333          * the browser console.
9334          *
9335          * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9336          * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9337          *
9338          * ## Example:
9339          *
9340          * ```js
9341          *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9342          *     return function(exception, cause) {
9343          *       exception.message += ' (caused by "' + cause + '")';
9344          *       throw exception;
9345          *     };
9346          *   });
9347          * ```
9348          *
9349          * This example will override the normal action of `$exceptionHandler`, to make angular
9350          * exceptions fail hard when they happen, instead of just logging to the console.
9351          *
9352          * <hr />
9353          * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9354          * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9355          * (unless executed during a digest).
9356          *
9357          * If you wish, you can manually delegate exceptions, e.g.
9358          * `try { ... } catch(e) { $exceptionHandler(e); }`
9359          *
9360          * @param {Error} exception Exception associated with the error.
9361          * @param {string=} cause optional information about the context in which
9362          *       the error was thrown.
9363          *
9364          */
9365         function $ExceptionHandlerProvider() {
9366           this.$get = ['$log', function($log) {
9367             return function(exception, cause) {
9368               $log.error.apply($log, arguments);
9369             };
9370           }];
9371         }
9372
9373         var $$ForceReflowProvider = function() {
9374           this.$get = ['$document', function($document) {
9375             return function(domNode) {
9376               //the line below will force the browser to perform a repaint so
9377               //that all the animated elements within the animation frame will
9378               //be properly updated and drawn on screen. This is required to
9379               //ensure that the preparation animation is properly flushed so that
9380               //the active state picks up from there. DO NOT REMOVE THIS LINE.
9381               //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9382               //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9383               //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9384               if (domNode) {
9385                 if (!domNode.nodeType && domNode instanceof jqLite) {
9386                   domNode = domNode[0];
9387                 }
9388               } else {
9389                 domNode = $document[0].body;
9390               }
9391               return domNode.offsetWidth + 1;
9392             };
9393           }];
9394         };
9395
9396         var APPLICATION_JSON = 'application/json';
9397         var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9398         var JSON_START = /^\[|^\{(?!\{)/;
9399         var JSON_ENDS = {
9400           '[': /]$/,
9401           '{': /}$/
9402         };
9403         var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9404         var $httpMinErr = minErr('$http');
9405         var $httpMinErrLegacyFn = function(method) {
9406           return function() {
9407             throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9408           };
9409         };
9410
9411         function serializeValue(v) {
9412           if (isObject(v)) {
9413             return isDate(v) ? v.toISOString() : toJson(v);
9414           }
9415           return v;
9416         }
9417
9418
9419         function $HttpParamSerializerProvider() {
9420           /**
9421            * @ngdoc service
9422            * @name $httpParamSerializer
9423            * @description
9424            *
9425            * Default {@link $http `$http`} params serializer that converts objects to strings
9426            * according to the following rules:
9427            *
9428            * * `{'foo': 'bar'}` results in `foo=bar`
9429            * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9430            * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9431            * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9432            *
9433            * Note that serializer will sort the request parameters alphabetically.
9434            * */
9435
9436           this.$get = function() {
9437             return function ngParamSerializer(params) {
9438               if (!params) return '';
9439               var parts = [];
9440               forEachSorted(params, function(value, key) {
9441                 if (value === null || isUndefined(value)) return;
9442                 if (isArray(value)) {
9443                   forEach(value, function(v, k) {
9444                     parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9445                   });
9446                 } else {
9447                   parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9448                 }
9449               });
9450
9451               return parts.join('&');
9452             };
9453           };
9454         }
9455
9456         function $HttpParamSerializerJQLikeProvider() {
9457           /**
9458            * @ngdoc service
9459            * @name $httpParamSerializerJQLike
9460            * @description
9461            *
9462            * Alternative {@link $http `$http`} params serializer that follows
9463            * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9464            * The serializer will also sort the params alphabetically.
9465            *
9466            * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9467            *
9468            * ```js
9469            * $http({
9470            *   url: myUrl,
9471            *   method: 'GET',
9472            *   params: myParams,
9473            *   paramSerializer: '$httpParamSerializerJQLike'
9474            * });
9475            * ```
9476            *
9477            * It is also possible to set it as the default `paramSerializer` in the
9478            * {@link $httpProvider#defaults `$httpProvider`}.
9479            *
9480            * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9481            * form data for submission:
9482            *
9483            * ```js
9484            * .controller(function($http, $httpParamSerializerJQLike) {
9485            *   //...
9486            *
9487            *   $http({
9488            *     url: myUrl,
9489            *     method: 'POST',
9490            *     data: $httpParamSerializerJQLike(myData),
9491            *     headers: {
9492            *       'Content-Type': 'application/x-www-form-urlencoded'
9493            *     }
9494            *   });
9495            *
9496            * });
9497            * ```
9498            *
9499            * */
9500           this.$get = function() {
9501             return function jQueryLikeParamSerializer(params) {
9502               if (!params) return '';
9503               var parts = [];
9504               serialize(params, '', true);
9505               return parts.join('&');
9506
9507               function serialize(toSerialize, prefix, topLevel) {
9508                 if (toSerialize === null || isUndefined(toSerialize)) return;
9509                 if (isArray(toSerialize)) {
9510                   forEach(toSerialize, function(value, index) {
9511                     serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9512                   });
9513                 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9514                   forEachSorted(toSerialize, function(value, key) {
9515                     serialize(value, prefix +
9516                         (topLevel ? '' : '[') +
9517                         key +
9518                         (topLevel ? '' : ']'));
9519                   });
9520                 } else {
9521                   parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9522                 }
9523               }
9524             };
9525           };
9526         }
9527
9528         function defaultHttpResponseTransform(data, headers) {
9529           if (isString(data)) {
9530             // Strip json vulnerability protection prefix and trim whitespace
9531             var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9532
9533             if (tempData) {
9534               var contentType = headers('Content-Type');
9535               if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9536                 data = fromJson(tempData);
9537               }
9538             }
9539           }
9540
9541           return data;
9542         }
9543
9544         function isJsonLike(str) {
9545             var jsonStart = str.match(JSON_START);
9546             return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9547         }
9548
9549         /**
9550          * Parse headers into key value object
9551          *
9552          * @param {string} headers Raw headers as a string
9553          * @returns {Object} Parsed headers as key value object
9554          */
9555         function parseHeaders(headers) {
9556           var parsed = createMap(), i;
9557
9558           function fillInParsed(key, val) {
9559             if (key) {
9560               parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9561             }
9562           }
9563
9564           if (isString(headers)) {
9565             forEach(headers.split('\n'), function(line) {
9566               i = line.indexOf(':');
9567               fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9568             });
9569           } else if (isObject(headers)) {
9570             forEach(headers, function(headerVal, headerKey) {
9571               fillInParsed(lowercase(headerKey), trim(headerVal));
9572             });
9573           }
9574
9575           return parsed;
9576         }
9577
9578
9579         /**
9580          * Returns a function that provides access to parsed headers.
9581          *
9582          * Headers are lazy parsed when first requested.
9583          * @see parseHeaders
9584          *
9585          * @param {(string|Object)} headers Headers to provide access to.
9586          * @returns {function(string=)} Returns a getter function which if called with:
9587          *
9588          *   - if called with single an argument returns a single header value or null
9589          *   - if called with no arguments returns an object containing all headers.
9590          */
9591         function headersGetter(headers) {
9592           var headersObj;
9593
9594           return function(name) {
9595             if (!headersObj) headersObj =  parseHeaders(headers);
9596
9597             if (name) {
9598               var value = headersObj[lowercase(name)];
9599               if (value === void 0) {
9600                 value = null;
9601               }
9602               return value;
9603             }
9604
9605             return headersObj;
9606           };
9607         }
9608
9609
9610         /**
9611          * Chain all given functions
9612          *
9613          * This function is used for both request and response transforming
9614          *
9615          * @param {*} data Data to transform.
9616          * @param {function(string=)} headers HTTP headers getter fn.
9617          * @param {number} status HTTP status code of the response.
9618          * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9619          * @returns {*} Transformed data.
9620          */
9621         function transformData(data, headers, status, fns) {
9622           if (isFunction(fns)) {
9623             return fns(data, headers, status);
9624           }
9625
9626           forEach(fns, function(fn) {
9627             data = fn(data, headers, status);
9628           });
9629
9630           return data;
9631         }
9632
9633
9634         function isSuccess(status) {
9635           return 200 <= status && status < 300;
9636         }
9637
9638
9639         /**
9640          * @ngdoc provider
9641          * @name $httpProvider
9642          * @description
9643          * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9644          * */
9645         function $HttpProvider() {
9646           /**
9647            * @ngdoc property
9648            * @name $httpProvider#defaults
9649            * @description
9650            *
9651            * Object containing default values for all {@link ng.$http $http} requests.
9652            *
9653            * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9654            * that will provide the cache for all requests who set their `cache` property to `true`.
9655            * If you set the `defaults.cache = false` then only requests that specify their own custom
9656            * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9657            *
9658            * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9659            * Defaults value is `'XSRF-TOKEN'`.
9660            *
9661            * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9662            * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9663            *
9664            * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9665            * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9666            * setting default headers.
9667            *     - **`defaults.headers.common`**
9668            *     - **`defaults.headers.post`**
9669            *     - **`defaults.headers.put`**
9670            *     - **`defaults.headers.patch`**
9671            *
9672            *
9673            * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9674            *  used to the prepare string representation of request parameters (specified as an object).
9675            *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9676            *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9677            *
9678            **/
9679           var defaults = this.defaults = {
9680             // transform incoming response data
9681             transformResponse: [defaultHttpResponseTransform],
9682
9683             // transform outgoing request data
9684             transformRequest: [function(d) {
9685               return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9686             }],
9687
9688             // default headers
9689             headers: {
9690               common: {
9691                 'Accept': 'application/json, text/plain, */*'
9692               },
9693               post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9694               put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9695               patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9696             },
9697
9698             xsrfCookieName: 'XSRF-TOKEN',
9699             xsrfHeaderName: 'X-XSRF-TOKEN',
9700
9701             paramSerializer: '$httpParamSerializer'
9702           };
9703
9704           var useApplyAsync = false;
9705           /**
9706            * @ngdoc method
9707            * @name $httpProvider#useApplyAsync
9708            * @description
9709            *
9710            * Configure $http service to combine processing of multiple http responses received at around
9711            * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9712            * significant performance improvement for bigger applications that make many HTTP requests
9713            * concurrently (common during application bootstrap).
9714            *
9715            * Defaults to false. If no value is specified, returns the current configured value.
9716            *
9717            * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9718            *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9719            *    to load and share the same digest cycle.
9720            *
9721            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9722            *    otherwise, returns the current configured value.
9723            **/
9724           this.useApplyAsync = function(value) {
9725             if (isDefined(value)) {
9726               useApplyAsync = !!value;
9727               return this;
9728             }
9729             return useApplyAsync;
9730           };
9731
9732           var useLegacyPromise = true;
9733           /**
9734            * @ngdoc method
9735            * @name $httpProvider#useLegacyPromiseExtensions
9736            * @description
9737            *
9738            * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9739            * This should be used to make sure that applications work without these methods.
9740            *
9741            * Defaults to true. If no value is specified, returns the current configured value.
9742            *
9743            * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9744            *
9745            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9746            *    otherwise, returns the current configured value.
9747            **/
9748           this.useLegacyPromiseExtensions = function(value) {
9749             if (isDefined(value)) {
9750               useLegacyPromise = !!value;
9751               return this;
9752             }
9753             return useLegacyPromise;
9754           };
9755
9756           /**
9757            * @ngdoc property
9758            * @name $httpProvider#interceptors
9759            * @description
9760            *
9761            * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9762            * pre-processing of request or postprocessing of responses.
9763            *
9764            * These service factories are ordered by request, i.e. they are applied in the same order as the
9765            * array, on request, but reverse order, on response.
9766            *
9767            * {@link ng.$http#interceptors Interceptors detailed info}
9768            **/
9769           var interceptorFactories = this.interceptors = [];
9770
9771           this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9772               function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9773
9774             var defaultCache = $cacheFactory('$http');
9775
9776             /**
9777              * Make sure that default param serializer is exposed as a function
9778              */
9779             defaults.paramSerializer = isString(defaults.paramSerializer) ?
9780               $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9781
9782             /**
9783              * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9784              * The reversal is needed so that we can build up the interception chain around the
9785              * server request.
9786              */
9787             var reversedInterceptors = [];
9788
9789             forEach(interceptorFactories, function(interceptorFactory) {
9790               reversedInterceptors.unshift(isString(interceptorFactory)
9791                   ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9792             });
9793
9794             /**
9795              * @ngdoc service
9796              * @kind function
9797              * @name $http
9798              * @requires ng.$httpBackend
9799              * @requires $cacheFactory
9800              * @requires $rootScope
9801              * @requires $q
9802              * @requires $injector
9803              *
9804              * @description
9805              * The `$http` service is a core Angular service that facilitates communication with the remote
9806              * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9807              * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9808              *
9809              * For unit testing applications that use `$http` service, see
9810              * {@link ngMock.$httpBackend $httpBackend mock}.
9811              *
9812              * For a higher level of abstraction, please check out the {@link ngResource.$resource
9813              * $resource} service.
9814              *
9815              * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9816              * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9817              * it is important to familiarize yourself with these APIs and the guarantees they provide.
9818              *
9819              *
9820              * ## General usage
9821              * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9822              * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9823              *
9824              * ```js
9825              *   // Simple GET request example:
9826              *   $http({
9827              *     method: 'GET',
9828              *     url: '/someUrl'
9829              *   }).then(function successCallback(response) {
9830              *       // this callback will be called asynchronously
9831              *       // when the response is available
9832              *     }, function errorCallback(response) {
9833              *       // called asynchronously if an error occurs
9834              *       // or server returns response with an error status.
9835              *     });
9836              * ```
9837              *
9838              * The response object has these properties:
9839              *
9840              *   - **data** – `{string|Object}` – The response body transformed with the transform
9841              *     functions.
9842              *   - **status** – `{number}` – HTTP status code of the response.
9843              *   - **headers** – `{function([headerName])}` – Header getter function.
9844              *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9845              *   - **statusText** – `{string}` – HTTP status text of the response.
9846              *
9847              * A response status code between 200 and 299 is considered a success status and
9848              * will result in the success callback being called. Note that if the response is a redirect,
9849              * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9850              * called for such responses.
9851              *
9852              *
9853              * ## Shortcut methods
9854              *
9855              * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9856              * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9857              * last argument.
9858              *
9859              * ```js
9860              *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9861              *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9862              * ```
9863              *
9864              * Complete list of shortcut methods:
9865              *
9866              * - {@link ng.$http#get $http.get}
9867              * - {@link ng.$http#head $http.head}
9868              * - {@link ng.$http#post $http.post}
9869              * - {@link ng.$http#put $http.put}
9870              * - {@link ng.$http#delete $http.delete}
9871              * - {@link ng.$http#jsonp $http.jsonp}
9872              * - {@link ng.$http#patch $http.patch}
9873              *
9874              *
9875              * ## Writing Unit Tests that use $http
9876              * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9877              * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9878              * request using trained responses.
9879              *
9880              * ```
9881              * $httpBackend.expectGET(...);
9882              * $http.get(...);
9883              * $httpBackend.flush();
9884              * ```
9885              *
9886              * ## Deprecation Notice
9887              * <div class="alert alert-danger">
9888              *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9889              *   Use the standard `then` method instead.
9890              *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9891              *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9892              * </div>
9893              *
9894              * ## Setting HTTP Headers
9895              *
9896              * The $http service will automatically add certain HTTP headers to all requests. These defaults
9897              * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9898              * object, which currently contains this default configuration:
9899              *
9900              * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9901              *   - `Accept: application/json, text/plain, * / *`
9902              * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9903              *   - `Content-Type: application/json`
9904              * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9905              *   - `Content-Type: application/json`
9906              *
9907              * To add or overwrite these defaults, simply add or remove a property from these configuration
9908              * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9909              * with the lowercased HTTP method name as the key, e.g.
9910              * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9911              *
9912              * The defaults can also be set at runtime via the `$http.defaults` object in the same
9913              * fashion. For example:
9914              *
9915              * ```
9916              * module.run(function($http) {
9917              *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9918              * });
9919              * ```
9920              *
9921              * In addition, you can supply a `headers` property in the config object passed when
9922              * calling `$http(config)`, which overrides the defaults without changing them globally.
9923              *
9924              * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9925              * Use the `headers` property, setting the desired header to `undefined`. For example:
9926              *
9927              * ```js
9928              * var req = {
9929              *  method: 'POST',
9930              *  url: 'http://example.com',
9931              *  headers: {
9932              *    'Content-Type': undefined
9933              *  },
9934              *  data: { test: 'test' }
9935              * }
9936              *
9937              * $http(req).then(function(){...}, function(){...});
9938              * ```
9939              *
9940              * ## Transforming Requests and Responses
9941              *
9942              * Both requests and responses can be transformed using transformation functions: `transformRequest`
9943              * and `transformResponse`. These properties can be a single function that returns
9944              * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9945              * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9946              *
9947              * ### Default Transformations
9948              *
9949              * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9950              * `defaults.transformResponse` properties. If a request does not provide its own transformations
9951              * then these will be applied.
9952              *
9953              * You can augment or replace the default transformations by modifying these properties by adding to or
9954              * replacing the array.
9955              *
9956              * Angular provides the following default transformations:
9957              *
9958              * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9959              *
9960              * - If the `data` property of the request configuration object contains an object, serialize it
9961              *   into JSON format.
9962              *
9963              * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9964              *
9965              *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9966              *  - If JSON response is detected, deserialize it using a JSON parser.
9967              *
9968              *
9969              * ### Overriding the Default Transformations Per Request
9970              *
9971              * If you wish override the request/response transformations only for a single request then provide
9972              * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9973              * into `$http`.
9974              *
9975              * Note that if you provide these properties on the config object the default transformations will be
9976              * overwritten. If you wish to augment the default transformations then you must include them in your
9977              * local transformation array.
9978              *
9979              * The following code demonstrates adding a new response transformation to be run after the default response
9980              * transformations have been run.
9981              *
9982              * ```js
9983              * function appendTransform(defaults, transform) {
9984              *
9985              *   // We can't guarantee that the default transformation is an array
9986              *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9987              *
9988              *   // Append the new transformation to the defaults
9989              *   return defaults.concat(transform);
9990              * }
9991              *
9992              * $http({
9993              *   url: '...',
9994              *   method: 'GET',
9995              *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9996              *     return doTransform(value);
9997              *   })
9998              * });
9999              * ```
10000              *
10001              *
10002              * ## Caching
10003              *
10004              * To enable caching, set the request configuration `cache` property to `true` (to use default
10005              * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10006              * When the cache is enabled, `$http` stores the response from the server in the specified
10007              * cache. The next time the same request is made, the response is served from the cache without
10008              * sending a request to the server.
10009              *
10010              * Note that even if the response is served from cache, delivery of the data is asynchronous in
10011              * the same way that real requests are.
10012              *
10013              * If there are multiple GET requests for the same URL that should be cached using the same
10014              * cache, but the cache is not populated yet, only one request to the server will be made and
10015              * the remaining requests will be fulfilled using the response from the first request.
10016              *
10017              * You can change the default cache to a new object (built with
10018              * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10019              * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10020              * their `cache` property to `true` will now use this cache object.
10021              *
10022              * If you set the default cache to `false` then only requests that specify their own custom
10023              * cache object will be cached.
10024              *
10025              * ## Interceptors
10026              *
10027              * Before you start creating interceptors, be sure to understand the
10028              * {@link ng.$q $q and deferred/promise APIs}.
10029              *
10030              * For purposes of global error handling, authentication, or any kind of synchronous or
10031              * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10032              * able to intercept requests before they are handed to the server and
10033              * responses before they are handed over to the application code that
10034              * initiated these requests. The interceptors leverage the {@link ng.$q
10035              * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10036              *
10037              * The interceptors are service factories that are registered with the `$httpProvider` by
10038              * adding them to the `$httpProvider.interceptors` array. The factory is called and
10039              * injected with dependencies (if specified) and returns the interceptor.
10040              *
10041              * There are two kinds of interceptors (and two kinds of rejection interceptors):
10042              *
10043              *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10044              *     modify the `config` object or create a new one. The function needs to return the `config`
10045              *     object directly, or a promise containing the `config` or a new `config` object.
10046              *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10047              *     resolved with a rejection.
10048              *   * `response`: interceptors get called with http `response` object. The function is free to
10049              *     modify the `response` object or create a new one. The function needs to return the `response`
10050              *     object directly, or as a promise containing the `response` or a new `response` object.
10051              *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10052              *     resolved with a rejection.
10053              *
10054              *
10055              * ```js
10056              *   // register the interceptor as a service
10057              *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10058              *     return {
10059              *       // optional method
10060              *       'request': function(config) {
10061              *         // do something on success
10062              *         return config;
10063              *       },
10064              *
10065              *       // optional method
10066              *      'requestError': function(rejection) {
10067              *         // do something on error
10068              *         if (canRecover(rejection)) {
10069              *           return responseOrNewPromise
10070              *         }
10071              *         return $q.reject(rejection);
10072              *       },
10073              *
10074              *
10075              *
10076              *       // optional method
10077              *       'response': function(response) {
10078              *         // do something on success
10079              *         return response;
10080              *       },
10081              *
10082              *       // optional method
10083              *      'responseError': function(rejection) {
10084              *         // do something on error
10085              *         if (canRecover(rejection)) {
10086              *           return responseOrNewPromise
10087              *         }
10088              *         return $q.reject(rejection);
10089              *       }
10090              *     };
10091              *   });
10092              *
10093              *   $httpProvider.interceptors.push('myHttpInterceptor');
10094              *
10095              *
10096              *   // alternatively, register the interceptor via an anonymous factory
10097              *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10098              *     return {
10099              *      'request': function(config) {
10100              *          // same as above
10101              *       },
10102              *
10103              *       'response': function(response) {
10104              *          // same as above
10105              *       }
10106              *     };
10107              *   });
10108              * ```
10109              *
10110              * ## Security Considerations
10111              *
10112              * When designing web applications, consider security threats from:
10113              *
10114              * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10115              * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10116              *
10117              * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10118              * pre-configured with strategies that address these issues, but for this to work backend server
10119              * cooperation is required.
10120              *
10121              * ### JSON Vulnerability Protection
10122              *
10123              * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10124              * allows third party website to turn your JSON resource URL into
10125              * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10126              * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10127              * Angular will automatically strip the prefix before processing it as JSON.
10128              *
10129              * For example if your server needs to return:
10130              * ```js
10131              * ['one','two']
10132              * ```
10133              *
10134              * which is vulnerable to attack, your server can return:
10135              * ```js
10136              * )]}',
10137              * ['one','two']
10138              * ```
10139              *
10140              * Angular will strip the prefix, before processing the JSON.
10141              *
10142              *
10143              * ### Cross Site Request Forgery (XSRF) Protection
10144              *
10145              * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10146              * an unauthorized site can gain your user's private data. Angular provides a mechanism
10147              * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10148              * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10149              * JavaScript that runs on your domain could read the cookie, your server can be assured that
10150              * the XHR came from JavaScript running on your domain. The header will not be set for
10151              * cross-domain requests.
10152              *
10153              * To take advantage of this, your server needs to set a token in a JavaScript readable session
10154              * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10155              * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10156              * that only JavaScript running on your domain could have sent the request. The token must be
10157              * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10158              * making up its own tokens). We recommend that the token is a digest of your site's
10159              * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10160              * for added security.
10161              *
10162              * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10163              * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10164              * or the per-request config object.
10165              *
10166              * In order to prevent collisions in environments where multiple Angular apps share the
10167              * same domain or subdomain, we recommend that each application uses unique cookie name.
10168              *
10169              * @param {object} config Object describing the request to be made and how it should be
10170              *    processed. The object has following properties:
10171              *
10172              *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10173              *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10174              *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10175              *      with the `paramSerializer` and appended as GET parameters.
10176              *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10177              *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10178              *      HTTP headers to send to the server. If the return value of a function is null, the
10179              *      header will not be sent. Functions accept a config object as an argument.
10180              *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10181              *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10182              *    - **transformRequest** –
10183              *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10184              *      transform function or an array of such functions. The transform function takes the http
10185              *      request body and headers and returns its transformed (typically serialized) version.
10186              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10187              *      Overriding the Default Transformations}
10188              *    - **transformResponse** –
10189              *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10190              *      transform function or an array of such functions. The transform function takes the http
10191              *      response body, headers and status and returns its transformed (typically deserialized) version.
10192              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10193              *      Overriding the Default TransformationjqLiks}
10194              *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10195              *      prepare the string representation of request parameters (specified as an object).
10196              *      If specified as string, it is interpreted as function registered with the
10197              *      {@link $injector $injector}, which means you can create your own serializer
10198              *      by registering it as a {@link auto.$provide#service service}.
10199              *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10200              *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10201              *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10202              *      GET request, otherwise if a cache instance built with
10203              *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10204              *      caching.
10205              *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10206              *      that should abort the request when resolved.
10207              *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10208              *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10209              *      for more information.
10210              *    - **responseType** - `{string}` - see
10211              *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10212              *
10213              * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10214              *                        when the request succeeds or fails.
10215              *
10216              *
10217              * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10218              *   requests. This is primarily meant to be used for debugging purposes.
10219              *
10220              *
10221              * @example
10222         <example module="httpExample">
10223         <file name="index.html">
10224           <div ng-controller="FetchController">
10225             <select ng-model="method" aria-label="Request method">
10226               <option>GET</option>
10227               <option>JSONP</option>
10228             </select>
10229             <input type="text" ng-model="url" size="80" aria-label="URL" />
10230             <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10231             <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10232             <button id="samplejsonpbtn"
10233               ng-click="updateModel('JSONP',
10234                             'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10235               Sample JSONP
10236             </button>
10237             <button id="invalidjsonpbtn"
10238               ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10239                 Invalid JSONP
10240               </button>
10241             <pre>http status code: {{status}}</pre>
10242             <pre>http response data: {{data}}</pre>
10243           </div>
10244         </file>
10245         <file name="script.js">
10246           angular.module('httpExample', [])
10247             .controller('FetchController', ['$scope', '$http', '$templateCache',
10248               function($scope, $http, $templateCache) {
10249                 $scope.method = 'GET';
10250                 $scope.url = 'http-hello.html';
10251
10252                 $scope.fetch = function() {
10253                   $scope.code = null;
10254                   $scope.response = null;
10255
10256                   $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10257                     then(function(response) {
10258                       $scope.status = response.status;
10259                       $scope.data = response.data;
10260                     }, function(response) {
10261                       $scope.data = response.data || "Request failed";
10262                       $scope.status = response.status;
10263                   });
10264                 };
10265
10266                 $scope.updateModel = function(method, url) {
10267                   $scope.method = method;
10268                   $scope.url = url;
10269                 };
10270               }]);
10271         </file>
10272         <file name="http-hello.html">
10273           Hello, $http!
10274         </file>
10275         <file name="protractor.js" type="protractor">
10276           var status = element(by.binding('status'));
10277           var data = element(by.binding('data'));
10278           var fetchBtn = element(by.id('fetchbtn'));
10279           var sampleGetBtn = element(by.id('samplegetbtn'));
10280           var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10281           var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10282
10283           it('should make an xhr GET request', function() {
10284             sampleGetBtn.click();
10285             fetchBtn.click();
10286             expect(status.getText()).toMatch('200');
10287             expect(data.getText()).toMatch(/Hello, \$http!/);
10288           });
10289
10290         // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10291         // it('should make a JSONP request to angularjs.org', function() {
10292         //   sampleJsonpBtn.click();
10293         //   fetchBtn.click();
10294         //   expect(status.getText()).toMatch('200');
10295         //   expect(data.getText()).toMatch(/Super Hero!/);
10296         // });
10297
10298           it('should make JSONP request to invalid URL and invoke the error handler',
10299               function() {
10300             invalidJsonpBtn.click();
10301             fetchBtn.click();
10302             expect(status.getText()).toMatch('0');
10303             expect(data.getText()).toMatch('Request failed');
10304           });
10305         </file>
10306         </example>
10307              */
10308             function $http(requestConfig) {
10309
10310               if (!angular.isObject(requestConfig)) {
10311                 throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10312               }
10313
10314               var config = extend({
10315                 method: 'get',
10316                 transformRequest: defaults.transformRequest,
10317                 transformResponse: defaults.transformResponse,
10318                 paramSerializer: defaults.paramSerializer
10319               }, requestConfig);
10320
10321               config.headers = mergeHeaders(requestConfig);
10322               config.method = uppercase(config.method);
10323               config.paramSerializer = isString(config.paramSerializer) ?
10324                 $injector.get(config.paramSerializer) : config.paramSerializer;
10325
10326               var serverRequest = function(config) {
10327                 var headers = config.headers;
10328                 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10329
10330                 // strip content-type if data is undefined
10331                 if (isUndefined(reqData)) {
10332                   forEach(headers, function(value, header) {
10333                     if (lowercase(header) === 'content-type') {
10334                         delete headers[header];
10335                     }
10336                   });
10337                 }
10338
10339                 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10340                   config.withCredentials = defaults.withCredentials;
10341                 }
10342
10343                 // send request
10344                 return sendReq(config, reqData).then(transformResponse, transformResponse);
10345               };
10346
10347               var chain = [serverRequest, undefined];
10348               var promise = $q.when(config);
10349
10350               // apply interceptors
10351               forEach(reversedInterceptors, function(interceptor) {
10352                 if (interceptor.request || interceptor.requestError) {
10353                   chain.unshift(interceptor.request, interceptor.requestError);
10354                 }
10355                 if (interceptor.response || interceptor.responseError) {
10356                   chain.push(interceptor.response, interceptor.responseError);
10357                 }
10358               });
10359
10360               while (chain.length) {
10361                 var thenFn = chain.shift();
10362                 var rejectFn = chain.shift();
10363
10364                 promise = promise.then(thenFn, rejectFn);
10365               }
10366
10367               if (useLegacyPromise) {
10368                 promise.success = function(fn) {
10369                   assertArgFn(fn, 'fn');
10370
10371                   promise.then(function(response) {
10372                     fn(response.data, response.status, response.headers, config);
10373                   });
10374                   return promise;
10375                 };
10376
10377                 promise.error = function(fn) {
10378                   assertArgFn(fn, 'fn');
10379
10380                   promise.then(null, function(response) {
10381                     fn(response.data, response.status, response.headers, config);
10382                   });
10383                   return promise;
10384                 };
10385               } else {
10386                 promise.success = $httpMinErrLegacyFn('success');
10387                 promise.error = $httpMinErrLegacyFn('error');
10388               }
10389
10390               return promise;
10391
10392               function transformResponse(response) {
10393                 // make a copy since the response must be cacheable
10394                 var resp = extend({}, response);
10395                 resp.data = transformData(response.data, response.headers, response.status,
10396                                           config.transformResponse);
10397                 return (isSuccess(response.status))
10398                   ? resp
10399                   : $q.reject(resp);
10400               }
10401
10402               function executeHeaderFns(headers, config) {
10403                 var headerContent, processedHeaders = {};
10404
10405                 forEach(headers, function(headerFn, header) {
10406                   if (isFunction(headerFn)) {
10407                     headerContent = headerFn(config);
10408                     if (headerContent != null) {
10409                       processedHeaders[header] = headerContent;
10410                     }
10411                   } else {
10412                     processedHeaders[header] = headerFn;
10413                   }
10414                 });
10415
10416                 return processedHeaders;
10417               }
10418
10419               function mergeHeaders(config) {
10420                 var defHeaders = defaults.headers,
10421                     reqHeaders = extend({}, config.headers),
10422                     defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10423
10424                 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10425
10426                 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10427                 defaultHeadersIteration:
10428                 for (defHeaderName in defHeaders) {
10429                   lowercaseDefHeaderName = lowercase(defHeaderName);
10430
10431                   for (reqHeaderName in reqHeaders) {
10432                     if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10433                       continue defaultHeadersIteration;
10434                     }
10435                   }
10436
10437                   reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10438                 }
10439
10440                 // execute if header value is a function for merged headers
10441                 return executeHeaderFns(reqHeaders, shallowCopy(config));
10442               }
10443             }
10444
10445             $http.pendingRequests = [];
10446
10447             /**
10448              * @ngdoc method
10449              * @name $http#get
10450              *
10451              * @description
10452              * Shortcut method to perform `GET` request.
10453              *
10454              * @param {string} url Relative or absolute URL specifying the destination of the request
10455              * @param {Object=} config Optional configuration object
10456              * @returns {HttpPromise} Future object
10457              */
10458
10459             /**
10460              * @ngdoc method
10461              * @name $http#delete
10462              *
10463              * @description
10464              * Shortcut method to perform `DELETE` request.
10465              *
10466              * @param {string} url Relative or absolute URL specifying the destination of the request
10467              * @param {Object=} config Optional configuration object
10468              * @returns {HttpPromise} Future object
10469              */
10470
10471             /**
10472              * @ngdoc method
10473              * @name $http#head
10474              *
10475              * @description
10476              * Shortcut method to perform `HEAD` request.
10477              *
10478              * @param {string} url Relative or absolute URL specifying the destination of the request
10479              * @param {Object=} config Optional configuration object
10480              * @returns {HttpPromise} Future object
10481              */
10482
10483             /**
10484              * @ngdoc method
10485              * @name $http#jsonp
10486              *
10487              * @description
10488              * Shortcut method to perform `JSONP` request.
10489              *
10490              * @param {string} url Relative or absolute URL specifying the destination of the request.
10491              *                     The name of the callback should be the string `JSON_CALLBACK`.
10492              * @param {Object=} config Optional configuration object
10493              * @returns {HttpPromise} Future object
10494              */
10495             createShortMethods('get', 'delete', 'head', 'jsonp');
10496
10497             /**
10498              * @ngdoc method
10499              * @name $http#post
10500              *
10501              * @description
10502              * Shortcut method to perform `POST` request.
10503              *
10504              * @param {string} url Relative or absolute URL specifying the destination of the request
10505              * @param {*} data Request content
10506              * @param {Object=} config Optional configuration object
10507              * @returns {HttpPromise} Future object
10508              */
10509
10510             /**
10511              * @ngdoc method
10512              * @name $http#put
10513              *
10514              * @description
10515              * Shortcut method to perform `PUT` request.
10516              *
10517              * @param {string} url Relative or absolute URL specifying the destination of the request
10518              * @param {*} data Request content
10519              * @param {Object=} config Optional configuration object
10520              * @returns {HttpPromise} Future object
10521              */
10522
10523              /**
10524               * @ngdoc method
10525               * @name $http#patch
10526               *
10527               * @description
10528               * Shortcut method to perform `PATCH` request.
10529               *
10530               * @param {string} url Relative or absolute URL specifying the destination of the request
10531               * @param {*} data Request content
10532               * @param {Object=} config Optional configuration object
10533               * @returns {HttpPromise} Future object
10534               */
10535             createShortMethodsWithData('post', 'put', 'patch');
10536
10537                 /**
10538                  * @ngdoc property
10539                  * @name $http#defaults
10540                  *
10541                  * @description
10542                  * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10543                  * default headers, withCredentials as well as request and response transformations.
10544                  *
10545                  * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10546                  */
10547             $http.defaults = defaults;
10548
10549
10550             return $http;
10551
10552
10553             function createShortMethods(names) {
10554               forEach(arguments, function(name) {
10555                 $http[name] = function(url, config) {
10556                   return $http(extend({}, config || {}, {
10557                     method: name,
10558                     url: url
10559                   }));
10560                 };
10561               });
10562             }
10563
10564
10565             function createShortMethodsWithData(name) {
10566               forEach(arguments, function(name) {
10567                 $http[name] = function(url, data, config) {
10568                   return $http(extend({}, config || {}, {
10569                     method: name,
10570                     url: url,
10571                     data: data
10572                   }));
10573                 };
10574               });
10575             }
10576
10577
10578             /**
10579              * Makes the request.
10580              *
10581              * !!! ACCESSES CLOSURE VARS:
10582              * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10583              */
10584             function sendReq(config, reqData) {
10585               var deferred = $q.defer(),
10586                   promise = deferred.promise,
10587                   cache,
10588                   cachedResp,
10589                   reqHeaders = config.headers,
10590                   url = buildUrl(config.url, config.paramSerializer(config.params));
10591
10592               $http.pendingRequests.push(config);
10593               promise.then(removePendingReq, removePendingReq);
10594
10595
10596               if ((config.cache || defaults.cache) && config.cache !== false &&
10597                   (config.method === 'GET' || config.method === 'JSONP')) {
10598                 cache = isObject(config.cache) ? config.cache
10599                       : isObject(defaults.cache) ? defaults.cache
10600                       : defaultCache;
10601               }
10602
10603               if (cache) {
10604                 cachedResp = cache.get(url);
10605                 if (isDefined(cachedResp)) {
10606                   if (isPromiseLike(cachedResp)) {
10607                     // cached request has already been sent, but there is no response yet
10608                     cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10609                   } else {
10610                     // serving from cache
10611                     if (isArray(cachedResp)) {
10612                       resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10613                     } else {
10614                       resolvePromise(cachedResp, 200, {}, 'OK');
10615                     }
10616                   }
10617                 } else {
10618                   // put the promise for the non-transformed response into cache as a placeholder
10619                   cache.put(url, promise);
10620                 }
10621               }
10622
10623
10624               // if we won't have the response in cache, set the xsrf headers and
10625               // send the request to the backend
10626               if (isUndefined(cachedResp)) {
10627                 var xsrfValue = urlIsSameOrigin(config.url)
10628                     ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10629                     : undefined;
10630                 if (xsrfValue) {
10631                   reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10632                 }
10633
10634                 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10635                     config.withCredentials, config.responseType);
10636               }
10637
10638               return promise;
10639
10640
10641               /**
10642                * Callback registered to $httpBackend():
10643                *  - caches the response if desired
10644                *  - resolves the raw $http promise
10645                *  - calls $apply
10646                */
10647               function done(status, response, headersString, statusText) {
10648                 if (cache) {
10649                   if (isSuccess(status)) {
10650                     cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10651                   } else {
10652                     // remove promise from the cache
10653                     cache.remove(url);
10654                   }
10655                 }
10656
10657                 function resolveHttpPromise() {
10658                   resolvePromise(response, status, headersString, statusText);
10659                 }
10660
10661                 if (useApplyAsync) {
10662                   $rootScope.$applyAsync(resolveHttpPromise);
10663                 } else {
10664                   resolveHttpPromise();
10665                   if (!$rootScope.$$phase) $rootScope.$apply();
10666                 }
10667               }
10668
10669
10670               /**
10671                * Resolves the raw $http promise.
10672                */
10673               function resolvePromise(response, status, headers, statusText) {
10674                 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10675                 status = status >= -1 ? status : 0;
10676
10677                 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10678                   data: response,
10679                   status: status,
10680                   headers: headersGetter(headers),
10681                   config: config,
10682                   statusText: statusText
10683                 });
10684               }
10685
10686               function resolvePromiseWithResult(result) {
10687                 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10688               }
10689
10690               function removePendingReq() {
10691                 var idx = $http.pendingRequests.indexOf(config);
10692                 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10693               }
10694             }
10695
10696
10697             function buildUrl(url, serializedParams) {
10698               if (serializedParams.length > 0) {
10699                 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10700               }
10701               return url;
10702             }
10703           }];
10704         }
10705
10706         /**
10707          * @ngdoc service
10708          * @name $xhrFactory
10709          *
10710          * @description
10711          * Factory function used to create XMLHttpRequest objects.
10712          *
10713          * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10714          *
10715          * ```
10716          * angular.module('myApp', [])
10717          * .factory('$xhrFactory', function() {
10718          *   return function createXhr(method, url) {
10719          *     return new window.XMLHttpRequest({mozSystem: true});
10720          *   };
10721          * });
10722          * ```
10723          *
10724          * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10725          * @param {string} url URL of the request.
10726          */
10727         function $xhrFactoryProvider() {
10728           this.$get = function() {
10729             return function createXhr() {
10730               return new window.XMLHttpRequest();
10731             };
10732           };
10733         }
10734
10735         /**
10736          * @ngdoc service
10737          * @name $httpBackend
10738          * @requires $window
10739          * @requires $document
10740          * @requires $xhrFactory
10741          *
10742          * @description
10743          * HTTP backend used by the {@link ng.$http service} that delegates to
10744          * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10745          *
10746          * You should never need to use this service directly, instead use the higher-level abstractions:
10747          * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10748          *
10749          * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10750          * $httpBackend} which can be trained with responses.
10751          */
10752         function $HttpBackendProvider() {
10753           this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10754             return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10755           }];
10756         }
10757
10758         function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10759           // TODO(vojta): fix the signature
10760           return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10761             $browser.$$incOutstandingRequestCount();
10762             url = url || $browser.url();
10763
10764             if (lowercase(method) == 'jsonp') {
10765               var callbackId = '_' + (callbacks.counter++).toString(36);
10766               callbacks[callbackId] = function(data) {
10767                 callbacks[callbackId].data = data;
10768                 callbacks[callbackId].called = true;
10769               };
10770
10771               var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10772                   callbackId, function(status, text) {
10773                 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10774                 callbacks[callbackId] = noop;
10775               });
10776             } else {
10777
10778               var xhr = createXhr(method, url);
10779
10780               xhr.open(method, url, true);
10781               forEach(headers, function(value, key) {
10782                 if (isDefined(value)) {
10783                     xhr.setRequestHeader(key, value);
10784                 }
10785               });
10786
10787               xhr.onload = function requestLoaded() {
10788                 var statusText = xhr.statusText || '';
10789
10790                 // responseText is the old-school way of retrieving response (supported by IE9)
10791                 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10792                 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10793
10794                 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10795                 var status = xhr.status === 1223 ? 204 : xhr.status;
10796
10797                 // fix status code when it is 0 (0 status is undocumented).
10798                 // Occurs when accessing file resources or on Android 4.1 stock browser
10799                 // while retrieving files from application cache.
10800                 if (status === 0) {
10801                   status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10802                 }
10803
10804                 completeRequest(callback,
10805                     status,
10806                     response,
10807                     xhr.getAllResponseHeaders(),
10808                     statusText);
10809               };
10810
10811               var requestError = function() {
10812                 // The response is always empty
10813                 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10814                 completeRequest(callback, -1, null, null, '');
10815               };
10816
10817               xhr.onerror = requestError;
10818               xhr.onabort = requestError;
10819
10820               if (withCredentials) {
10821                 xhr.withCredentials = true;
10822               }
10823
10824               if (responseType) {
10825                 try {
10826                   xhr.responseType = responseType;
10827                 } catch (e) {
10828                   // WebKit added support for the json responseType value on 09/03/2013
10829                   // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10830                   // known to throw when setting the value "json" as the response type. Other older
10831                   // browsers implementing the responseType
10832                   //
10833                   // The json response type can be ignored if not supported, because JSON payloads are
10834                   // parsed on the client-side regardless.
10835                   if (responseType !== 'json') {
10836                     throw e;
10837                   }
10838                 }
10839               }
10840
10841               xhr.send(isUndefined(post) ? null : post);
10842             }
10843
10844             if (timeout > 0) {
10845               var timeoutId = $browserDefer(timeoutRequest, timeout);
10846             } else if (isPromiseLike(timeout)) {
10847               timeout.then(timeoutRequest);
10848             }
10849
10850
10851             function timeoutRequest() {
10852               jsonpDone && jsonpDone();
10853               xhr && xhr.abort();
10854             }
10855
10856             function completeRequest(callback, status, response, headersString, statusText) {
10857               // cancel timeout and subsequent timeout promise resolution
10858               if (isDefined(timeoutId)) {
10859                 $browserDefer.cancel(timeoutId);
10860               }
10861               jsonpDone = xhr = null;
10862
10863               callback(status, response, headersString, statusText);
10864               $browser.$$completeOutstandingRequest(noop);
10865             }
10866           };
10867
10868           function jsonpReq(url, callbackId, done) {
10869             // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10870             // - fetches local scripts via XHR and evals them
10871             // - adds and immediately removes script elements from the document
10872             var script = rawDocument.createElement('script'), callback = null;
10873             script.type = "text/javascript";
10874             script.src = url;
10875             script.async = true;
10876
10877             callback = function(event) {
10878               removeEventListenerFn(script, "load", callback);
10879               removeEventListenerFn(script, "error", callback);
10880               rawDocument.body.removeChild(script);
10881               script = null;
10882               var status = -1;
10883               var text = "unknown";
10884
10885               if (event) {
10886                 if (event.type === "load" && !callbacks[callbackId].called) {
10887                   event = { type: "error" };
10888                 }
10889                 text = event.type;
10890                 status = event.type === "error" ? 404 : 200;
10891               }
10892
10893               if (done) {
10894                 done(status, text);
10895               }
10896             };
10897
10898             addEventListenerFn(script, "load", callback);
10899             addEventListenerFn(script, "error", callback);
10900             rawDocument.body.appendChild(script);
10901             return callback;
10902           }
10903         }
10904
10905         var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10906         $interpolateMinErr.throwNoconcat = function(text) {
10907           throw $interpolateMinErr('noconcat',
10908               "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10909               "interpolations that concatenate multiple expressions when a trusted value is " +
10910               "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10911         };
10912
10913         $interpolateMinErr.interr = function(text, err) {
10914           return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10915         };
10916
10917         /**
10918          * @ngdoc provider
10919          * @name $interpolateProvider
10920          *
10921          * @description
10922          *
10923          * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10924          *
10925          * @example
10926         <example module="customInterpolationApp">
10927         <file name="index.html">
10928         <script>
10929           var customInterpolationApp = angular.module('customInterpolationApp', []);
10930
10931           customInterpolationApp.config(function($interpolateProvider) {
10932             $interpolateProvider.startSymbol('//');
10933             $interpolateProvider.endSymbol('//');
10934           });
10935
10936
10937           customInterpolationApp.controller('DemoController', function() {
10938               this.label = "This binding is brought you by // interpolation symbols.";
10939           });
10940         </script>
10941         <div ng-app="App" ng-controller="DemoController as demo">
10942             //demo.label//
10943         </div>
10944         </file>
10945         <file name="protractor.js" type="protractor">
10946           it('should interpolate binding with custom symbols', function() {
10947             expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10948           });
10949         </file>
10950         </example>
10951          */
10952         function $InterpolateProvider() {
10953           var startSymbol = '{{';
10954           var endSymbol = '}}';
10955
10956           /**
10957            * @ngdoc method
10958            * @name $interpolateProvider#startSymbol
10959            * @description
10960            * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10961            *
10962            * @param {string=} value new value to set the starting symbol to.
10963            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10964            */
10965           this.startSymbol = function(value) {
10966             if (value) {
10967               startSymbol = value;
10968               return this;
10969             } else {
10970               return startSymbol;
10971             }
10972           };
10973
10974           /**
10975            * @ngdoc method
10976            * @name $interpolateProvider#endSymbol
10977            * @description
10978            * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10979            *
10980            * @param {string=} value new value to set the ending symbol to.
10981            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10982            */
10983           this.endSymbol = function(value) {
10984             if (value) {
10985               endSymbol = value;
10986               return this;
10987             } else {
10988               return endSymbol;
10989             }
10990           };
10991
10992
10993           this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10994             var startSymbolLength = startSymbol.length,
10995                 endSymbolLength = endSymbol.length,
10996                 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10997                 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10998
10999             function escape(ch) {
11000               return '\\\\\\' + ch;
11001             }
11002
11003             function unescapeText(text) {
11004               return text.replace(escapedStartRegexp, startSymbol).
11005                 replace(escapedEndRegexp, endSymbol);
11006             }
11007
11008             function stringify(value) {
11009               if (value == null) { // null || undefined
11010                 return '';
11011               }
11012               switch (typeof value) {
11013                 case 'string':
11014                   break;
11015                 case 'number':
11016                   value = '' + value;
11017                   break;
11018                 default:
11019                   value = toJson(value);
11020               }
11021
11022               return value;
11023             }
11024
11025             /**
11026              * @ngdoc service
11027              * @name $interpolate
11028              * @kind function
11029              *
11030              * @requires $parse
11031              * @requires $sce
11032              *
11033              * @description
11034              *
11035              * Compiles a string with markup into an interpolation function. This service is used by the
11036              * HTML {@link ng.$compile $compile} service for data binding. See
11037              * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11038              * interpolation markup.
11039              *
11040              *
11041              * ```js
11042              *   var $interpolate = ...; // injected
11043              *   var exp = $interpolate('Hello {{name | uppercase}}!');
11044              *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11045              * ```
11046              *
11047              * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11048              * `true`, the interpolation function will return `undefined` unless all embedded expressions
11049              * evaluate to a value other than `undefined`.
11050              *
11051              * ```js
11052              *   var $interpolate = ...; // injected
11053              *   var context = {greeting: 'Hello', name: undefined };
11054              *
11055              *   // default "forgiving" mode
11056              *   var exp = $interpolate('{{greeting}} {{name}}!');
11057              *   expect(exp(context)).toEqual('Hello !');
11058              *
11059              *   // "allOrNothing" mode
11060              *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11061              *   expect(exp(context)).toBeUndefined();
11062              *   context.name = 'Angular';
11063              *   expect(exp(context)).toEqual('Hello Angular!');
11064              * ```
11065              *
11066              * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11067              *
11068              * ####Escaped Interpolation
11069              * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11070              * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11071              * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11072              * or binding.
11073              *
11074              * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11075              * degree, while also enabling code examples to work without relying on the
11076              * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11077              *
11078              * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11079              * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11080              * interpolation start/end markers with their escaped counterparts.**
11081              *
11082              * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11083              * output when the $interpolate service processes the text. So, for HTML elements interpolated
11084              * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11085              * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11086              * this is typically useful only when user-data is used in rendering a template from the server, or
11087              * when otherwise untrusted data is used by a directive.
11088              *
11089              * <example>
11090              *  <file name="index.html">
11091              *    <div ng-init="username='A user'">
11092              *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11093              *        </p>
11094              *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11095              *        application, but fails to accomplish their task, because the server has correctly
11096              *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11097              *        characters.</p>
11098              *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11099              *        from the database by an administrator.</p>
11100              *    </div>
11101              *  </file>
11102              * </example>
11103              *
11104              * @param {string} text The text with markup to interpolate.
11105              * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11106              *    embedded expression in order to return an interpolation function. Strings with no
11107              *    embedded expression will return null for the interpolation function.
11108              * @param {string=} trustedContext when provided, the returned function passes the interpolated
11109              *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11110              *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11111              *    provides Strict Contextual Escaping for details.
11112              * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11113              *    unless all embedded expressions evaluate to a value other than `undefined`.
11114              * @returns {function(context)} an interpolation function which is used to compute the
11115              *    interpolated string. The function has these parameters:
11116              *
11117              * - `context`: evaluation context for all expressions embedded in the interpolated text
11118              */
11119             function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11120               allOrNothing = !!allOrNothing;
11121               var startIndex,
11122                   endIndex,
11123                   index = 0,
11124                   expressions = [],
11125                   parseFns = [],
11126                   textLength = text.length,
11127                   exp,
11128                   concat = [],
11129                   expressionPositions = [];
11130
11131               while (index < textLength) {
11132                 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11133                      ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11134                   if (index !== startIndex) {
11135                     concat.push(unescapeText(text.substring(index, startIndex)));
11136                   }
11137                   exp = text.substring(startIndex + startSymbolLength, endIndex);
11138                   expressions.push(exp);
11139                   parseFns.push($parse(exp, parseStringifyInterceptor));
11140                   index = endIndex + endSymbolLength;
11141                   expressionPositions.push(concat.length);
11142                   concat.push('');
11143                 } else {
11144                   // we did not find an interpolation, so we have to add the remainder to the separators array
11145                   if (index !== textLength) {
11146                     concat.push(unescapeText(text.substring(index)));
11147                   }
11148                   break;
11149                 }
11150               }
11151
11152               // Concatenating expressions makes it hard to reason about whether some combination of
11153               // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11154               // single expression be used for iframe[src], object[src], etc., we ensure that the value
11155               // that's used is assigned or constructed by some JS code somewhere that is more testable or
11156               // make it obvious that you bound the value to some user controlled value.  This helps reduce
11157               // the load when auditing for XSS issues.
11158               if (trustedContext && concat.length > 1) {
11159                   $interpolateMinErr.throwNoconcat(text);
11160               }
11161
11162               if (!mustHaveExpression || expressions.length) {
11163                 var compute = function(values) {
11164                   for (var i = 0, ii = expressions.length; i < ii; i++) {
11165                     if (allOrNothing && isUndefined(values[i])) return;
11166                     concat[expressionPositions[i]] = values[i];
11167                   }
11168                   return concat.join('');
11169                 };
11170
11171                 var getValue = function(value) {
11172                   return trustedContext ?
11173                     $sce.getTrusted(trustedContext, value) :
11174                     $sce.valueOf(value);
11175                 };
11176
11177                 return extend(function interpolationFn(context) {
11178                     var i = 0;
11179                     var ii = expressions.length;
11180                     var values = new Array(ii);
11181
11182                     try {
11183                       for (; i < ii; i++) {
11184                         values[i] = parseFns[i](context);
11185                       }
11186
11187                       return compute(values);
11188                     } catch (err) {
11189                       $exceptionHandler($interpolateMinErr.interr(text, err));
11190                     }
11191
11192                   }, {
11193                   // all of these properties are undocumented for now
11194                   exp: text, //just for compatibility with regular watchers created via $watch
11195                   expressions: expressions,
11196                   $$watchDelegate: function(scope, listener) {
11197                     var lastValue;
11198                     return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11199                       var currValue = compute(values);
11200                       if (isFunction(listener)) {
11201                         listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11202                       }
11203                       lastValue = currValue;
11204                     });
11205                   }
11206                 });
11207               }
11208
11209               function parseStringifyInterceptor(value) {
11210                 try {
11211                   value = getValue(value);
11212                   return allOrNothing && !isDefined(value) ? value : stringify(value);
11213                 } catch (err) {
11214                   $exceptionHandler($interpolateMinErr.interr(text, err));
11215                 }
11216               }
11217             }
11218
11219
11220             /**
11221              * @ngdoc method
11222              * @name $interpolate#startSymbol
11223              * @description
11224              * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11225              *
11226              * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11227              * the symbol.
11228              *
11229              * @returns {string} start symbol.
11230              */
11231             $interpolate.startSymbol = function() {
11232               return startSymbol;
11233             };
11234
11235
11236             /**
11237              * @ngdoc method
11238              * @name $interpolate#endSymbol
11239              * @description
11240              * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11241              *
11242              * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11243              * the symbol.
11244              *
11245              * @returns {string} end symbol.
11246              */
11247             $interpolate.endSymbol = function() {
11248               return endSymbol;
11249             };
11250
11251             return $interpolate;
11252           }];
11253         }
11254
11255         function $IntervalProvider() {
11256           this.$get = ['$rootScope', '$window', '$q', '$$q',
11257                function($rootScope,   $window,   $q,   $$q) {
11258             var intervals = {};
11259
11260
11261              /**
11262               * @ngdoc service
11263               * @name $interval
11264               *
11265               * @description
11266               * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11267               * milliseconds.
11268               *
11269               * The return value of registering an interval function is a promise. This promise will be
11270               * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11271               * run indefinitely if `count` is not defined. The value of the notification will be the
11272               * number of iterations that have run.
11273               * To cancel an interval, call `$interval.cancel(promise)`.
11274               *
11275               * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11276               * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11277               * time.
11278               *
11279               * <div class="alert alert-warning">
11280               * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11281               * with them.  In particular they are not automatically destroyed when a controller's scope or a
11282               * directive's element are destroyed.
11283               * You should take this into consideration and make sure to always cancel the interval at the
11284               * appropriate moment.  See the example below for more details on how and when to do this.
11285               * </div>
11286               *
11287               * @param {function()} fn A function that should be called repeatedly.
11288               * @param {number} delay Number of milliseconds between each function call.
11289               * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11290               *   indefinitely.
11291               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11292               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11293               * @param {...*=} Pass additional parameters to the executed function.
11294               * @returns {promise} A promise which will be notified on each iteration.
11295               *
11296               * @example
11297               * <example module="intervalExample">
11298               * <file name="index.html">
11299               *   <script>
11300               *     angular.module('intervalExample', [])
11301               *       .controller('ExampleController', ['$scope', '$interval',
11302               *         function($scope, $interval) {
11303               *           $scope.format = 'M/d/yy h:mm:ss a';
11304               *           $scope.blood_1 = 100;
11305               *           $scope.blood_2 = 120;
11306               *
11307               *           var stop;
11308               *           $scope.fight = function() {
11309               *             // Don't start a new fight if we are already fighting
11310               *             if ( angular.isDefined(stop) ) return;
11311               *
11312               *             stop = $interval(function() {
11313               *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11314               *                 $scope.blood_1 = $scope.blood_1 - 3;
11315               *                 $scope.blood_2 = $scope.blood_2 - 4;
11316               *               } else {
11317               *                 $scope.stopFight();
11318               *               }
11319               *             }, 100);
11320               *           };
11321               *
11322               *           $scope.stopFight = function() {
11323               *             if (angular.isDefined(stop)) {
11324               *               $interval.cancel(stop);
11325               *               stop = undefined;
11326               *             }
11327               *           };
11328               *
11329               *           $scope.resetFight = function() {
11330               *             $scope.blood_1 = 100;
11331               *             $scope.blood_2 = 120;
11332               *           };
11333               *
11334               *           $scope.$on('$destroy', function() {
11335               *             // Make sure that the interval is destroyed too
11336               *             $scope.stopFight();
11337               *           });
11338               *         }])
11339               *       // Register the 'myCurrentTime' directive factory method.
11340               *       // We inject $interval and dateFilter service since the factory method is DI.
11341               *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11342               *         function($interval, dateFilter) {
11343               *           // return the directive link function. (compile function not needed)
11344               *           return function(scope, element, attrs) {
11345               *             var format,  // date format
11346               *                 stopTime; // so that we can cancel the time updates
11347               *
11348               *             // used to update the UI
11349               *             function updateTime() {
11350               *               element.text(dateFilter(new Date(), format));
11351               *             }
11352               *
11353               *             // watch the expression, and update the UI on change.
11354               *             scope.$watch(attrs.myCurrentTime, function(value) {
11355               *               format = value;
11356               *               updateTime();
11357               *             });
11358               *
11359               *             stopTime = $interval(updateTime, 1000);
11360               *
11361               *             // listen on DOM destroy (removal) event, and cancel the next UI update
11362               *             // to prevent updating time after the DOM element was removed.
11363               *             element.on('$destroy', function() {
11364               *               $interval.cancel(stopTime);
11365               *             });
11366               *           }
11367               *         }]);
11368               *   </script>
11369               *
11370               *   <div>
11371               *     <div ng-controller="ExampleController">
11372               *       <label>Date format: <input ng-model="format"></label> <hr/>
11373               *       Current time is: <span my-current-time="format"></span>
11374               *       <hr/>
11375               *       Blood 1 : <font color='red'>{{blood_1}}</font>
11376               *       Blood 2 : <font color='red'>{{blood_2}}</font>
11377               *       <button type="button" data-ng-click="fight()">Fight</button>
11378               *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11379               *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11380               *     </div>
11381               *   </div>
11382               *
11383               * </file>
11384               * </example>
11385               */
11386             function interval(fn, delay, count, invokeApply) {
11387               var hasParams = arguments.length > 4,
11388                   args = hasParams ? sliceArgs(arguments, 4) : [],
11389                   setInterval = $window.setInterval,
11390                   clearInterval = $window.clearInterval,
11391                   iteration = 0,
11392                   skipApply = (isDefined(invokeApply) && !invokeApply),
11393                   deferred = (skipApply ? $$q : $q).defer(),
11394                   promise = deferred.promise;
11395
11396               count = isDefined(count) ? count : 0;
11397
11398               promise.then(null, null, (!hasParams) ? fn : function() {
11399                 fn.apply(null, args);
11400               });
11401
11402               promise.$$intervalId = setInterval(function tick() {
11403                 deferred.notify(iteration++);
11404
11405                 if (count > 0 && iteration >= count) {
11406                   deferred.resolve(iteration);
11407                   clearInterval(promise.$$intervalId);
11408                   delete intervals[promise.$$intervalId];
11409                 }
11410
11411                 if (!skipApply) $rootScope.$apply();
11412
11413               }, delay);
11414
11415               intervals[promise.$$intervalId] = deferred;
11416
11417               return promise;
11418             }
11419
11420
11421              /**
11422               * @ngdoc method
11423               * @name $interval#cancel
11424               *
11425               * @description
11426               * Cancels a task associated with the `promise`.
11427               *
11428               * @param {Promise=} promise returned by the `$interval` function.
11429               * @returns {boolean} Returns `true` if the task was successfully canceled.
11430               */
11431             interval.cancel = function(promise) {
11432               if (promise && promise.$$intervalId in intervals) {
11433                 intervals[promise.$$intervalId].reject('canceled');
11434                 $window.clearInterval(promise.$$intervalId);
11435                 delete intervals[promise.$$intervalId];
11436                 return true;
11437               }
11438               return false;
11439             };
11440
11441             return interval;
11442           }];
11443         }
11444
11445         /**
11446          * @ngdoc service
11447          * @name $locale
11448          *
11449          * @description
11450          * $locale service provides localization rules for various Angular components. As of right now the
11451          * only public api is:
11452          *
11453          * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11454          */
11455
11456         var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11457             DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11458         var $locationMinErr = minErr('$location');
11459
11460
11461         /**
11462          * Encode path using encodeUriSegment, ignoring forward slashes
11463          *
11464          * @param {string} path Path to encode
11465          * @returns {string}
11466          */
11467         function encodePath(path) {
11468           var segments = path.split('/'),
11469               i = segments.length;
11470
11471           while (i--) {
11472             segments[i] = encodeUriSegment(segments[i]);
11473           }
11474
11475           return segments.join('/');
11476         }
11477
11478         function parseAbsoluteUrl(absoluteUrl, locationObj) {
11479           var parsedUrl = urlResolve(absoluteUrl);
11480
11481           locationObj.$$protocol = parsedUrl.protocol;
11482           locationObj.$$host = parsedUrl.hostname;
11483           locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11484         }
11485
11486
11487         function parseAppUrl(relativeUrl, locationObj) {
11488           var prefixed = (relativeUrl.charAt(0) !== '/');
11489           if (prefixed) {
11490             relativeUrl = '/' + relativeUrl;
11491           }
11492           var match = urlResolve(relativeUrl);
11493           locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11494               match.pathname.substring(1) : match.pathname);
11495           locationObj.$$search = parseKeyValue(match.search);
11496           locationObj.$$hash = decodeURIComponent(match.hash);
11497
11498           // make sure path starts with '/';
11499           if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11500             locationObj.$$path = '/' + locationObj.$$path;
11501           }
11502         }
11503
11504
11505         /**
11506          *
11507          * @param {string} begin
11508          * @param {string} whole
11509          * @returns {string} returns text from whole after begin or undefined if it does not begin with
11510          *                   expected string.
11511          */
11512         function beginsWith(begin, whole) {
11513           if (whole.indexOf(begin) === 0) {
11514             return whole.substr(begin.length);
11515           }
11516         }
11517
11518
11519         function stripHash(url) {
11520           var index = url.indexOf('#');
11521           return index == -1 ? url : url.substr(0, index);
11522         }
11523
11524         function trimEmptyHash(url) {
11525           return url.replace(/(#.+)|#$/, '$1');
11526         }
11527
11528
11529         function stripFile(url) {
11530           return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11531         }
11532
11533         /* return the server only (scheme://host:port) */
11534         function serverBase(url) {
11535           return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11536         }
11537
11538
11539         /**
11540          * LocationHtml5Url represents an url
11541          * This object is exposed as $location service when HTML5 mode is enabled and supported
11542          *
11543          * @constructor
11544          * @param {string} appBase application base URL
11545          * @param {string} appBaseNoFile application base URL stripped of any filename
11546          * @param {string} basePrefix url path prefix
11547          */
11548         function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11549           this.$$html5 = true;
11550           basePrefix = basePrefix || '';
11551           parseAbsoluteUrl(appBase, this);
11552
11553
11554           /**
11555            * Parse given html5 (regular) url string into properties
11556            * @param {string} url HTML5 url
11557            * @private
11558            */
11559           this.$$parse = function(url) {
11560             var pathUrl = beginsWith(appBaseNoFile, url);
11561             if (!isString(pathUrl)) {
11562               throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11563                   appBaseNoFile);
11564             }
11565
11566             parseAppUrl(pathUrl, this);
11567
11568             if (!this.$$path) {
11569               this.$$path = '/';
11570             }
11571
11572             this.$$compose();
11573           };
11574
11575           /**
11576            * Compose url and update `absUrl` property
11577            * @private
11578            */
11579           this.$$compose = function() {
11580             var search = toKeyValue(this.$$search),
11581                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11582
11583             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11584             this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11585           };
11586
11587           this.$$parseLinkUrl = function(url, relHref) {
11588             if (relHref && relHref[0] === '#') {
11589               // special case for links to hash fragments:
11590               // keep the old url and only replace the hash fragment
11591               this.hash(relHref.slice(1));
11592               return true;
11593             }
11594             var appUrl, prevAppUrl;
11595             var rewrittenUrl;
11596
11597             if (isDefined(appUrl = beginsWith(appBase, url))) {
11598               prevAppUrl = appUrl;
11599               if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11600                 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11601               } else {
11602                 rewrittenUrl = appBase + prevAppUrl;
11603               }
11604             } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11605               rewrittenUrl = appBaseNoFile + appUrl;
11606             } else if (appBaseNoFile == url + '/') {
11607               rewrittenUrl = appBaseNoFile;
11608             }
11609             if (rewrittenUrl) {
11610               this.$$parse(rewrittenUrl);
11611             }
11612             return !!rewrittenUrl;
11613           };
11614         }
11615
11616
11617         /**
11618          * LocationHashbangUrl represents url
11619          * This object is exposed as $location service when developer doesn't opt into html5 mode.
11620          * It also serves as the base class for html5 mode fallback on legacy browsers.
11621          *
11622          * @constructor
11623          * @param {string} appBase application base URL
11624          * @param {string} appBaseNoFile application base URL stripped of any filename
11625          * @param {string} hashPrefix hashbang prefix
11626          */
11627         function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11628
11629           parseAbsoluteUrl(appBase, this);
11630
11631
11632           /**
11633            * Parse given hashbang url into properties
11634            * @param {string} url Hashbang url
11635            * @private
11636            */
11637           this.$$parse = function(url) {
11638             var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11639             var withoutHashUrl;
11640
11641             if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11642
11643               // The rest of the url starts with a hash so we have
11644               // got either a hashbang path or a plain hash fragment
11645               withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11646               if (isUndefined(withoutHashUrl)) {
11647                 // There was no hashbang prefix so we just have a hash fragment
11648                 withoutHashUrl = withoutBaseUrl;
11649               }
11650
11651             } else {
11652               // There was no hashbang path nor hash fragment:
11653               // If we are in HTML5 mode we use what is left as the path;
11654               // Otherwise we ignore what is left
11655               if (this.$$html5) {
11656                 withoutHashUrl = withoutBaseUrl;
11657               } else {
11658                 withoutHashUrl = '';
11659                 if (isUndefined(withoutBaseUrl)) {
11660                   appBase = url;
11661                   this.replace();
11662                 }
11663               }
11664             }
11665
11666             parseAppUrl(withoutHashUrl, this);
11667
11668             this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11669
11670             this.$$compose();
11671
11672             /*
11673              * In Windows, on an anchor node on documents loaded from
11674              * the filesystem, the browser will return a pathname
11675              * prefixed with the drive name ('/C:/path') when a
11676              * pathname without a drive is set:
11677              *  * a.setAttribute('href', '/foo')
11678              *   * a.pathname === '/C:/foo' //true
11679              *
11680              * Inside of Angular, we're always using pathnames that
11681              * do not include drive names for routing.
11682              */
11683             function removeWindowsDriveName(path, url, base) {
11684               /*
11685               Matches paths for file protocol on windows,
11686               such as /C:/foo/bar, and captures only /foo/bar.
11687               */
11688               var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11689
11690               var firstPathSegmentMatch;
11691
11692               //Get the relative path from the input URL.
11693               if (url.indexOf(base) === 0) {
11694                 url = url.replace(base, '');
11695               }
11696
11697               // The input URL intentionally contains a first path segment that ends with a colon.
11698               if (windowsFilePathExp.exec(url)) {
11699                 return path;
11700               }
11701
11702               firstPathSegmentMatch = windowsFilePathExp.exec(path);
11703               return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11704             }
11705           };
11706
11707           /**
11708            * Compose hashbang url and update `absUrl` property
11709            * @private
11710            */
11711           this.$$compose = function() {
11712             var search = toKeyValue(this.$$search),
11713                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11714
11715             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11716             this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11717           };
11718
11719           this.$$parseLinkUrl = function(url, relHref) {
11720             if (stripHash(appBase) == stripHash(url)) {
11721               this.$$parse(url);
11722               return true;
11723             }
11724             return false;
11725           };
11726         }
11727
11728
11729         /**
11730          * LocationHashbangUrl represents url
11731          * This object is exposed as $location service when html5 history api is enabled but the browser
11732          * does not support it.
11733          *
11734          * @constructor
11735          * @param {string} appBase application base URL
11736          * @param {string} appBaseNoFile application base URL stripped of any filename
11737          * @param {string} hashPrefix hashbang prefix
11738          */
11739         function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11740           this.$$html5 = true;
11741           LocationHashbangUrl.apply(this, arguments);
11742
11743           this.$$parseLinkUrl = function(url, relHref) {
11744             if (relHref && relHref[0] === '#') {
11745               // special case for links to hash fragments:
11746               // keep the old url and only replace the hash fragment
11747               this.hash(relHref.slice(1));
11748               return true;
11749             }
11750
11751             var rewrittenUrl;
11752             var appUrl;
11753
11754             if (appBase == stripHash(url)) {
11755               rewrittenUrl = url;
11756             } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11757               rewrittenUrl = appBase + hashPrefix + appUrl;
11758             } else if (appBaseNoFile === url + '/') {
11759               rewrittenUrl = appBaseNoFile;
11760             }
11761             if (rewrittenUrl) {
11762               this.$$parse(rewrittenUrl);
11763             }
11764             return !!rewrittenUrl;
11765           };
11766
11767           this.$$compose = function() {
11768             var search = toKeyValue(this.$$search),
11769                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11770
11771             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11772             // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11773             this.$$absUrl = appBase + hashPrefix + this.$$url;
11774           };
11775
11776         }
11777
11778
11779         var locationPrototype = {
11780
11781           /**
11782            * Are we in html5 mode?
11783            * @private
11784            */
11785           $$html5: false,
11786
11787           /**
11788            * Has any change been replacing?
11789            * @private
11790            */
11791           $$replace: false,
11792
11793           /**
11794            * @ngdoc method
11795            * @name $location#absUrl
11796            *
11797            * @description
11798            * This method is getter only.
11799            *
11800            * Return full url representation with all segments encoded according to rules specified in
11801            * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11802            *
11803            *
11804            * ```js
11805            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11806            * var absUrl = $location.absUrl();
11807            * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11808            * ```
11809            *
11810            * @return {string} full url
11811            */
11812           absUrl: locationGetter('$$absUrl'),
11813
11814           /**
11815            * @ngdoc method
11816            * @name $location#url
11817            *
11818            * @description
11819            * This method is getter / setter.
11820            *
11821            * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11822            *
11823            * Change path, search and hash, when called with parameter and return `$location`.
11824            *
11825            *
11826            * ```js
11827            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11828            * var url = $location.url();
11829            * // => "/some/path?foo=bar&baz=xoxo"
11830            * ```
11831            *
11832            * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11833            * @return {string} url
11834            */
11835           url: function(url) {
11836             if (isUndefined(url)) {
11837               return this.$$url;
11838             }
11839
11840             var match = PATH_MATCH.exec(url);
11841             if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11842             if (match[2] || match[1] || url === '') this.search(match[3] || '');
11843             this.hash(match[5] || '');
11844
11845             return this;
11846           },
11847
11848           /**
11849            * @ngdoc method
11850            * @name $location#protocol
11851            *
11852            * @description
11853            * This method is getter only.
11854            *
11855            * Return protocol of current url.
11856            *
11857            *
11858            * ```js
11859            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11860            * var protocol = $location.protocol();
11861            * // => "http"
11862            * ```
11863            *
11864            * @return {string} protocol of current url
11865            */
11866           protocol: locationGetter('$$protocol'),
11867
11868           /**
11869            * @ngdoc method
11870            * @name $location#host
11871            *
11872            * @description
11873            * This method is getter only.
11874            *
11875            * Return host of current url.
11876            *
11877            * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11878            *
11879            *
11880            * ```js
11881            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11882            * var host = $location.host();
11883            * // => "example.com"
11884            *
11885            * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11886            * host = $location.host();
11887            * // => "example.com"
11888            * host = location.host;
11889            * // => "example.com:8080"
11890            * ```
11891            *
11892            * @return {string} host of current url.
11893            */
11894           host: locationGetter('$$host'),
11895
11896           /**
11897            * @ngdoc method
11898            * @name $location#port
11899            *
11900            * @description
11901            * This method is getter only.
11902            *
11903            * Return port of current url.
11904            *
11905            *
11906            * ```js
11907            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11908            * var port = $location.port();
11909            * // => 80
11910            * ```
11911            *
11912            * @return {Number} port
11913            */
11914           port: locationGetter('$$port'),
11915
11916           /**
11917            * @ngdoc method
11918            * @name $location#path
11919            *
11920            * @description
11921            * This method is getter / setter.
11922            *
11923            * Return path of current url when called without any parameter.
11924            *
11925            * Change path when called with parameter and return `$location`.
11926            *
11927            * Note: Path should always begin with forward slash (/), this method will add the forward slash
11928            * if it is missing.
11929            *
11930            *
11931            * ```js
11932            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11933            * var path = $location.path();
11934            * // => "/some/path"
11935            * ```
11936            *
11937            * @param {(string|number)=} path New path
11938            * @return {string} path
11939            */
11940           path: locationGetterSetter('$$path', function(path) {
11941             path = path !== null ? path.toString() : '';
11942             return path.charAt(0) == '/' ? path : '/' + path;
11943           }),
11944
11945           /**
11946            * @ngdoc method
11947            * @name $location#search
11948            *
11949            * @description
11950            * This method is getter / setter.
11951            *
11952            * Return search part (as object) of current url when called without any parameter.
11953            *
11954            * Change search part when called with parameter and return `$location`.
11955            *
11956            *
11957            * ```js
11958            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11959            * var searchObject = $location.search();
11960            * // => {foo: 'bar', baz: 'xoxo'}
11961            *
11962            * // set foo to 'yipee'
11963            * $location.search('foo', 'yipee');
11964            * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11965            * ```
11966            *
11967            * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11968            * hash object.
11969            *
11970            * When called with a single argument the method acts as a setter, setting the `search` component
11971            * of `$location` to the specified value.
11972            *
11973            * If the argument is a hash object containing an array of values, these values will be encoded
11974            * as duplicate search parameters in the url.
11975            *
11976            * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11977            * will override only a single search property.
11978            *
11979            * If `paramValue` is an array, it will override the property of the `search` component of
11980            * `$location` specified via the first argument.
11981            *
11982            * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11983            *
11984            * If `paramValue` is `true`, the property specified via the first argument will be added with no
11985            * value nor trailing equal sign.
11986            *
11987            * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11988            * one or more arguments returns `$location` object itself.
11989            */
11990           search: function(search, paramValue) {
11991             switch (arguments.length) {
11992               case 0:
11993                 return this.$$search;
11994               case 1:
11995                 if (isString(search) || isNumber(search)) {
11996                   search = search.toString();
11997                   this.$$search = parseKeyValue(search);
11998                 } else if (isObject(search)) {
11999                   search = copy(search, {});
12000                   // remove object undefined or null properties
12001                   forEach(search, function(value, key) {
12002                     if (value == null) delete search[key];
12003                   });
12004
12005                   this.$$search = search;
12006                 } else {
12007                   throw $locationMinErr('isrcharg',
12008                       'The first argument of the `$location#search()` call must be a string or an object.');
12009                 }
12010                 break;
12011               default:
12012                 if (isUndefined(paramValue) || paramValue === null) {
12013                   delete this.$$search[search];
12014                 } else {
12015                   this.$$search[search] = paramValue;
12016                 }
12017             }
12018
12019             this.$$compose();
12020             return this;
12021           },
12022
12023           /**
12024            * @ngdoc method
12025            * @name $location#hash
12026            *
12027            * @description
12028            * This method is getter / setter.
12029            *
12030            * Returns the hash fragment when called without any parameters.
12031            *
12032            * Changes the hash fragment when called with a parameter and returns `$location`.
12033            *
12034            *
12035            * ```js
12036            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12037            * var hash = $location.hash();
12038            * // => "hashValue"
12039            * ```
12040            *
12041            * @param {(string|number)=} hash New hash fragment
12042            * @return {string} hash
12043            */
12044           hash: locationGetterSetter('$$hash', function(hash) {
12045             return hash !== null ? hash.toString() : '';
12046           }),
12047
12048           /**
12049            * @ngdoc method
12050            * @name $location#replace
12051            *
12052            * @description
12053            * If called, all changes to $location during the current `$digest` will replace the current history
12054            * record, instead of adding a new one.
12055            */
12056           replace: function() {
12057             this.$$replace = true;
12058             return this;
12059           }
12060         };
12061
12062         forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12063           Location.prototype = Object.create(locationPrototype);
12064
12065           /**
12066            * @ngdoc method
12067            * @name $location#state
12068            *
12069            * @description
12070            * This method is getter / setter.
12071            *
12072            * Return the history state object when called without any parameter.
12073            *
12074            * Change the history state object when called with one parameter and return `$location`.
12075            * The state object is later passed to `pushState` or `replaceState`.
12076            *
12077            * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12078            * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12079            * older browsers (like IE9 or Android < 4.0), don't use this method.
12080            *
12081            * @param {object=} state State object for pushState or replaceState
12082            * @return {object} state
12083            */
12084           Location.prototype.state = function(state) {
12085             if (!arguments.length) {
12086               return this.$$state;
12087             }
12088
12089             if (Location !== LocationHtml5Url || !this.$$html5) {
12090               throw $locationMinErr('nostate', 'History API state support is available only ' +
12091                 'in HTML5 mode and only in browsers supporting HTML5 History API');
12092             }
12093             // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12094             // but we're changing the $$state reference to $browser.state() during the $digest
12095             // so the modification window is narrow.
12096             this.$$state = isUndefined(state) ? null : state;
12097
12098             return this;
12099           };
12100         });
12101
12102
12103         function locationGetter(property) {
12104           return function() {
12105             return this[property];
12106           };
12107         }
12108
12109
12110         function locationGetterSetter(property, preprocess) {
12111           return function(value) {
12112             if (isUndefined(value)) {
12113               return this[property];
12114             }
12115
12116             this[property] = preprocess(value);
12117             this.$$compose();
12118
12119             return this;
12120           };
12121         }
12122
12123
12124         /**
12125          * @ngdoc service
12126          * @name $location
12127          *
12128          * @requires $rootElement
12129          *
12130          * @description
12131          * The $location service parses the URL in the browser address bar (based on the
12132          * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12133          * available to your application. Changes to the URL in the address bar are reflected into
12134          * $location service and changes to $location are reflected into the browser address bar.
12135          *
12136          * **The $location service:**
12137          *
12138          * - Exposes the current URL in the browser address bar, so you can
12139          *   - Watch and observe the URL.
12140          *   - Change the URL.
12141          * - Synchronizes the URL with the browser when the user
12142          *   - Changes the address bar.
12143          *   - Clicks the back or forward button (or clicks a History link).
12144          *   - Clicks on a link.
12145          * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12146          *
12147          * For more information see {@link guide/$location Developer Guide: Using $location}
12148          */
12149
12150         /**
12151          * @ngdoc provider
12152          * @name $locationProvider
12153          * @description
12154          * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12155          */
12156         function $LocationProvider() {
12157           var hashPrefix = '',
12158               html5Mode = {
12159                 enabled: false,
12160                 requireBase: true,
12161                 rewriteLinks: true
12162               };
12163
12164           /**
12165            * @ngdoc method
12166            * @name $locationProvider#hashPrefix
12167            * @description
12168            * @param {string=} prefix Prefix for hash part (containing path and search)
12169            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12170            */
12171           this.hashPrefix = function(prefix) {
12172             if (isDefined(prefix)) {
12173               hashPrefix = prefix;
12174               return this;
12175             } else {
12176               return hashPrefix;
12177             }
12178           };
12179
12180           /**
12181            * @ngdoc method
12182            * @name $locationProvider#html5Mode
12183            * @description
12184            * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12185            *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12186            *   properties:
12187            *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12188            *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12189            *     support `pushState`.
12190            *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12191            *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12192            *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12193            *     See the {@link guide/$location $location guide for more information}
12194            *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12195            *     enables/disables url rewriting for relative links.
12196            *
12197            * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12198            */
12199           this.html5Mode = function(mode) {
12200             if (isBoolean(mode)) {
12201               html5Mode.enabled = mode;
12202               return this;
12203             } else if (isObject(mode)) {
12204
12205               if (isBoolean(mode.enabled)) {
12206                 html5Mode.enabled = mode.enabled;
12207               }
12208
12209               if (isBoolean(mode.requireBase)) {
12210                 html5Mode.requireBase = mode.requireBase;
12211               }
12212
12213               if (isBoolean(mode.rewriteLinks)) {
12214                 html5Mode.rewriteLinks = mode.rewriteLinks;
12215               }
12216
12217               return this;
12218             } else {
12219               return html5Mode;
12220             }
12221           };
12222
12223           /**
12224            * @ngdoc event
12225            * @name $location#$locationChangeStart
12226            * @eventType broadcast on root scope
12227            * @description
12228            * Broadcasted before a URL will change.
12229            *
12230            * This change can be prevented by calling
12231            * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12232            * details about event object. Upon successful change
12233            * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12234            *
12235            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12236            * the browser supports the HTML5 History API.
12237            *
12238            * @param {Object} angularEvent Synthetic event object.
12239            * @param {string} newUrl New URL
12240            * @param {string=} oldUrl URL that was before it was changed.
12241            * @param {string=} newState New history state object
12242            * @param {string=} oldState History state object that was before it was changed.
12243            */
12244
12245           /**
12246            * @ngdoc event
12247            * @name $location#$locationChangeSuccess
12248            * @eventType broadcast on root scope
12249            * @description
12250            * Broadcasted after a URL was changed.
12251            *
12252            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12253            * the browser supports the HTML5 History API.
12254            *
12255            * @param {Object} angularEvent Synthetic event object.
12256            * @param {string} newUrl New URL
12257            * @param {string=} oldUrl URL that was before it was changed.
12258            * @param {string=} newState New history state object
12259            * @param {string=} oldState History state object that was before it was changed.
12260            */
12261
12262           this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12263               function($rootScope, $browser, $sniffer, $rootElement, $window) {
12264             var $location,
12265                 LocationMode,
12266                 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12267                 initialUrl = $browser.url(),
12268                 appBase;
12269
12270             if (html5Mode.enabled) {
12271               if (!baseHref && html5Mode.requireBase) {
12272                 throw $locationMinErr('nobase',
12273                   "$location in HTML5 mode requires a <base> tag to be present!");
12274               }
12275               appBase = serverBase(initialUrl) + (baseHref || '/');
12276               LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12277             } else {
12278               appBase = stripHash(initialUrl);
12279               LocationMode = LocationHashbangUrl;
12280             }
12281             var appBaseNoFile = stripFile(appBase);
12282
12283             $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12284             $location.$$parseLinkUrl(initialUrl, initialUrl);
12285
12286             $location.$$state = $browser.state();
12287
12288             var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12289
12290             function setBrowserUrlWithFallback(url, replace, state) {
12291               var oldUrl = $location.url();
12292               var oldState = $location.$$state;
12293               try {
12294                 $browser.url(url, replace, state);
12295
12296                 // Make sure $location.state() returns referentially identical (not just deeply equal)
12297                 // state object; this makes possible quick checking if the state changed in the digest
12298                 // loop. Checking deep equality would be too expensive.
12299                 $location.$$state = $browser.state();
12300               } catch (e) {
12301                 // Restore old values if pushState fails
12302                 $location.url(oldUrl);
12303                 $location.$$state = oldState;
12304
12305                 throw e;
12306               }
12307             }
12308
12309             $rootElement.on('click', function(event) {
12310               // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12311               // currently we open nice url link and redirect then
12312
12313               if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12314
12315               var elm = jqLite(event.target);
12316
12317               // traverse the DOM up to find first A tag
12318               while (nodeName_(elm[0]) !== 'a') {
12319                 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12320                 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12321               }
12322
12323               var absHref = elm.prop('href');
12324               // get the actual href attribute - see
12325               // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12326               var relHref = elm.attr('href') || elm.attr('xlink:href');
12327
12328               if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12329                 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12330                 // an animation.
12331                 absHref = urlResolve(absHref.animVal).href;
12332               }
12333
12334               // Ignore when url is started with javascript: or mailto:
12335               if (IGNORE_URI_REGEXP.test(absHref)) return;
12336
12337               if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12338                 if ($location.$$parseLinkUrl(absHref, relHref)) {
12339                   // We do a preventDefault for all urls that are part of the angular application,
12340                   // in html5mode and also without, so that we are able to abort navigation without
12341                   // getting double entries in the location history.
12342                   event.preventDefault();
12343                   // update location manually
12344                   if ($location.absUrl() != $browser.url()) {
12345                     $rootScope.$apply();
12346                     // hack to work around FF6 bug 684208 when scenario runner clicks on links
12347                     $window.angular['ff-684208-preventDefault'] = true;
12348                   }
12349                 }
12350               }
12351             });
12352
12353
12354             // rewrite hashbang url <> html5 url
12355             if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12356               $browser.url($location.absUrl(), true);
12357             }
12358
12359             var initializing = true;
12360
12361             // update $location when $browser url changes
12362             $browser.onUrlChange(function(newUrl, newState) {
12363
12364               if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12365                 // If we are navigating outside of the app then force a reload
12366                 $window.location.href = newUrl;
12367                 return;
12368               }
12369
12370               $rootScope.$evalAsync(function() {
12371                 var oldUrl = $location.absUrl();
12372                 var oldState = $location.$$state;
12373                 var defaultPrevented;
12374                 newUrl = trimEmptyHash(newUrl);
12375                 $location.$$parse(newUrl);
12376                 $location.$$state = newState;
12377
12378                 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12379                     newState, oldState).defaultPrevented;
12380
12381                 // if the location was changed by a `$locationChangeStart` handler then stop
12382                 // processing this location change
12383                 if ($location.absUrl() !== newUrl) return;
12384
12385                 if (defaultPrevented) {
12386                   $location.$$parse(oldUrl);
12387                   $location.$$state = oldState;
12388                   setBrowserUrlWithFallback(oldUrl, false, oldState);
12389                 } else {
12390                   initializing = false;
12391                   afterLocationChange(oldUrl, oldState);
12392                 }
12393               });
12394               if (!$rootScope.$$phase) $rootScope.$digest();
12395             });
12396
12397             // update browser
12398             $rootScope.$watch(function $locationWatch() {
12399               var oldUrl = trimEmptyHash($browser.url());
12400               var newUrl = trimEmptyHash($location.absUrl());
12401               var oldState = $browser.state();
12402               var currentReplace = $location.$$replace;
12403               var urlOrStateChanged = oldUrl !== newUrl ||
12404                 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12405
12406               if (initializing || urlOrStateChanged) {
12407                 initializing = false;
12408
12409                 $rootScope.$evalAsync(function() {
12410                   var newUrl = $location.absUrl();
12411                   var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12412                       $location.$$state, oldState).defaultPrevented;
12413
12414                   // if the location was changed by a `$locationChangeStart` handler then stop
12415                   // processing this location change
12416                   if ($location.absUrl() !== newUrl) return;
12417
12418                   if (defaultPrevented) {
12419                     $location.$$parse(oldUrl);
12420                     $location.$$state = oldState;
12421                   } else {
12422                     if (urlOrStateChanged) {
12423                       setBrowserUrlWithFallback(newUrl, currentReplace,
12424                                                 oldState === $location.$$state ? null : $location.$$state);
12425                     }
12426                     afterLocationChange(oldUrl, oldState);
12427                   }
12428                 });
12429               }
12430
12431               $location.$$replace = false;
12432
12433               // we don't need to return anything because $evalAsync will make the digest loop dirty when
12434               // there is a change
12435             });
12436
12437             return $location;
12438
12439             function afterLocationChange(oldUrl, oldState) {
12440               $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12441                 $location.$$state, oldState);
12442             }
12443         }];
12444         }
12445
12446         /**
12447          * @ngdoc service
12448          * @name $log
12449          * @requires $window
12450          *
12451          * @description
12452          * Simple service for logging. Default implementation safely writes the message
12453          * into the browser's console (if present).
12454          *
12455          * The main purpose of this service is to simplify debugging and troubleshooting.
12456          *
12457          * The default is to log `debug` messages. You can use
12458          * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12459          *
12460          * @example
12461            <example module="logExample">
12462              <file name="script.js">
12463                angular.module('logExample', [])
12464                  .controller('LogController', ['$scope', '$log', function($scope, $log) {
12465                    $scope.$log = $log;
12466                    $scope.message = 'Hello World!';
12467                  }]);
12468              </file>
12469              <file name="index.html">
12470                <div ng-controller="LogController">
12471                  <p>Reload this page with open console, enter text and hit the log button...</p>
12472                  <label>Message:
12473                  <input type="text" ng-model="message" /></label>
12474                  <button ng-click="$log.log(message)">log</button>
12475                  <button ng-click="$log.warn(message)">warn</button>
12476                  <button ng-click="$log.info(message)">info</button>
12477                  <button ng-click="$log.error(message)">error</button>
12478                  <button ng-click="$log.debug(message)">debug</button>
12479                </div>
12480              </file>
12481            </example>
12482          */
12483
12484         /**
12485          * @ngdoc provider
12486          * @name $logProvider
12487          * @description
12488          * Use the `$logProvider` to configure how the application logs messages
12489          */
12490         function $LogProvider() {
12491           var debug = true,
12492               self = this;
12493
12494           /**
12495            * @ngdoc method
12496            * @name $logProvider#debugEnabled
12497            * @description
12498            * @param {boolean=} flag enable or disable debug level messages
12499            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12500            */
12501           this.debugEnabled = function(flag) {
12502             if (isDefined(flag)) {
12503               debug = flag;
12504             return this;
12505             } else {
12506               return debug;
12507             }
12508           };
12509
12510           this.$get = ['$window', function($window) {
12511             return {
12512               /**
12513                * @ngdoc method
12514                * @name $log#log
12515                *
12516                * @description
12517                * Write a log message
12518                */
12519               log: consoleLog('log'),
12520
12521               /**
12522                * @ngdoc method
12523                * @name $log#info
12524                *
12525                * @description
12526                * Write an information message
12527                */
12528               info: consoleLog('info'),
12529
12530               /**
12531                * @ngdoc method
12532                * @name $log#warn
12533                *
12534                * @description
12535                * Write a warning message
12536                */
12537               warn: consoleLog('warn'),
12538
12539               /**
12540                * @ngdoc method
12541                * @name $log#error
12542                *
12543                * @description
12544                * Write an error message
12545                */
12546               error: consoleLog('error'),
12547
12548               /**
12549                * @ngdoc method
12550                * @name $log#debug
12551                *
12552                * @description
12553                * Write a debug message
12554                */
12555               debug: (function() {
12556                 var fn = consoleLog('debug');
12557
12558                 return function() {
12559                   if (debug) {
12560                     fn.apply(self, arguments);
12561                   }
12562                 };
12563               }())
12564             };
12565
12566             function formatError(arg) {
12567               if (arg instanceof Error) {
12568                 if (arg.stack) {
12569                   arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12570                       ? 'Error: ' + arg.message + '\n' + arg.stack
12571                       : arg.stack;
12572                 } else if (arg.sourceURL) {
12573                   arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12574                 }
12575               }
12576               return arg;
12577             }
12578
12579             function consoleLog(type) {
12580               var console = $window.console || {},
12581                   logFn = console[type] || console.log || noop,
12582                   hasApply = false;
12583
12584               // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12585               // The reason behind this is that console.log has type "object" in IE8...
12586               try {
12587                 hasApply = !!logFn.apply;
12588               } catch (e) {}
12589
12590               if (hasApply) {
12591                 return function() {
12592                   var args = [];
12593                   forEach(arguments, function(arg) {
12594                     args.push(formatError(arg));
12595                   });
12596                   return logFn.apply(console, args);
12597                 };
12598               }
12599
12600               // we are IE which either doesn't have window.console => this is noop and we do nothing,
12601               // or we are IE where console.log doesn't have apply so we log at least first 2 args
12602               return function(arg1, arg2) {
12603                 logFn(arg1, arg2 == null ? '' : arg2);
12604               };
12605             }
12606           }];
12607         }
12608
12609         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12610          *     Any commits to this file should be reviewed with security in mind.  *
12611          *   Changes to this file can potentially create security vulnerabilities. *
12612          *          An approval from 2 Core members with history of modifying      *
12613          *                         this file is required.                          *
12614          *                                                                         *
12615          *  Does the change somehow allow for arbitrary javascript to be executed? *
12616          *    Or allows for someone to change the prototype of built-in objects?   *
12617          *     Or gives undesired access to variables likes document or window?    *
12618          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12619
12620         var $parseMinErr = minErr('$parse');
12621
12622         // Sandboxing Angular Expressions
12623         // ------------------------------
12624         // Angular expressions are generally considered safe because these expressions only have direct
12625         // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12626         // obtaining a reference to native JS functions such as the Function constructor.
12627         //
12628         // As an example, consider the following Angular expression:
12629         //
12630         //   {}.toString.constructor('alert("evil JS code")')
12631         //
12632         // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12633         // against the expression language, but not to prevent exploits that were enabled by exposing
12634         // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12635         // practice and therefore we are not even trying to protect against interaction with an object
12636         // explicitly exposed in this way.
12637         //
12638         // In general, it is not possible to access a Window object from an angular expression unless a
12639         // window or some DOM object that has a reference to window is published onto a Scope.
12640         // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12641         // native objects.
12642         //
12643         // See https://docs.angularjs.org/guide/security
12644
12645
12646         function ensureSafeMemberName(name, fullExpression) {
12647           if (name === "__defineGetter__" || name === "__defineSetter__"
12648               || name === "__lookupGetter__" || name === "__lookupSetter__"
12649               || name === "__proto__") {
12650             throw $parseMinErr('isecfld',
12651                 'Attempting to access a disallowed field in Angular expressions! '
12652                 + 'Expression: {0}', fullExpression);
12653           }
12654           return name;
12655         }
12656
12657         function getStringValue(name, fullExpression) {
12658           // From the JavaScript docs:
12659           // Property names must be strings. This means that non-string objects cannot be used
12660           // as keys in an object. Any non-string object, including a number, is typecasted
12661           // into a string via the toString method.
12662           //
12663           // So, to ensure that we are checking the same `name` that JavaScript would use,
12664           // we cast it to a string, if possible.
12665           // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12666           // this is, this will handle objects that misbehave.
12667           name = name + '';
12668           if (!isString(name)) {
12669             throw $parseMinErr('iseccst',
12670                 'Cannot convert object to primitive value! '
12671                 + 'Expression: {0}', fullExpression);
12672           }
12673           return name;
12674         }
12675
12676         function ensureSafeObject(obj, fullExpression) {
12677           // nifty check if obj is Function that is fast and works across iframes and other contexts
12678           if (obj) {
12679             if (obj.constructor === obj) {
12680               throw $parseMinErr('isecfn',
12681                   'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12682                   fullExpression);
12683             } else if (// isWindow(obj)
12684                 obj.window === obj) {
12685               throw $parseMinErr('isecwindow',
12686                   'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12687                   fullExpression);
12688             } else if (// isElement(obj)
12689                 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12690               throw $parseMinErr('isecdom',
12691                   'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12692                   fullExpression);
12693             } else if (// block Object so that we can't get hold of dangerous Object.* methods
12694                 obj === Object) {
12695               throw $parseMinErr('isecobj',
12696                   'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12697                   fullExpression);
12698             }
12699           }
12700           return obj;
12701         }
12702
12703         var CALL = Function.prototype.call;
12704         var APPLY = Function.prototype.apply;
12705         var BIND = Function.prototype.bind;
12706
12707         function ensureSafeFunction(obj, fullExpression) {
12708           if (obj) {
12709             if (obj.constructor === obj) {
12710               throw $parseMinErr('isecfn',
12711                 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12712                 fullExpression);
12713             } else if (obj === CALL || obj === APPLY || obj === BIND) {
12714               throw $parseMinErr('isecff',
12715                 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12716                 fullExpression);
12717             }
12718           }
12719         }
12720
12721         function ensureSafeAssignContext(obj, fullExpression) {
12722           if (obj) {
12723             if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12724                 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12725               throw $parseMinErr('isecaf',
12726                 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12727             }
12728           }
12729         }
12730
12731         var OPERATORS = createMap();
12732         forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12733         var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12734
12735
12736         /////////////////////////////////////////
12737
12738
12739         /**
12740          * @constructor
12741          */
12742         var Lexer = function(options) {
12743           this.options = options;
12744         };
12745
12746         Lexer.prototype = {
12747           constructor: Lexer,
12748
12749           lex: function(text) {
12750             this.text = text;
12751             this.index = 0;
12752             this.tokens = [];
12753
12754             while (this.index < this.text.length) {
12755               var ch = this.text.charAt(this.index);
12756               if (ch === '"' || ch === "'") {
12757                 this.readString(ch);
12758               } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12759                 this.readNumber();
12760               } else if (this.isIdent(ch)) {
12761                 this.readIdent();
12762               } else if (this.is(ch, '(){}[].,;:?')) {
12763                 this.tokens.push({index: this.index, text: ch});
12764                 this.index++;
12765               } else if (this.isWhitespace(ch)) {
12766                 this.index++;
12767               } else {
12768                 var ch2 = ch + this.peek();
12769                 var ch3 = ch2 + this.peek(2);
12770                 var op1 = OPERATORS[ch];
12771                 var op2 = OPERATORS[ch2];
12772                 var op3 = OPERATORS[ch3];
12773                 if (op1 || op2 || op3) {
12774                   var token = op3 ? ch3 : (op2 ? ch2 : ch);
12775                   this.tokens.push({index: this.index, text: token, operator: true});
12776                   this.index += token.length;
12777                 } else {
12778                   this.throwError('Unexpected next character ', this.index, this.index + 1);
12779                 }
12780               }
12781             }
12782             return this.tokens;
12783           },
12784
12785           is: function(ch, chars) {
12786             return chars.indexOf(ch) !== -1;
12787           },
12788
12789           peek: function(i) {
12790             var num = i || 1;
12791             return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12792           },
12793
12794           isNumber: function(ch) {
12795             return ('0' <= ch && ch <= '9') && typeof ch === "string";
12796           },
12797
12798           isWhitespace: function(ch) {
12799             // IE treats non-breaking space as \u00A0
12800             return (ch === ' ' || ch === '\r' || ch === '\t' ||
12801                     ch === '\n' || ch === '\v' || ch === '\u00A0');
12802           },
12803
12804           isIdent: function(ch) {
12805             return ('a' <= ch && ch <= 'z' ||
12806                     'A' <= ch && ch <= 'Z' ||
12807                     '_' === ch || ch === '$');
12808           },
12809
12810           isExpOperator: function(ch) {
12811             return (ch === '-' || ch === '+' || this.isNumber(ch));
12812           },
12813
12814           throwError: function(error, start, end) {
12815             end = end || this.index;
12816             var colStr = (isDefined(start)
12817                     ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12818                     : ' ' + end);
12819             throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12820                 error, colStr, this.text);
12821           },
12822
12823           readNumber: function() {
12824             var number = '';
12825             var start = this.index;
12826             while (this.index < this.text.length) {
12827               var ch = lowercase(this.text.charAt(this.index));
12828               if (ch == '.' || this.isNumber(ch)) {
12829                 number += ch;
12830               } else {
12831                 var peekCh = this.peek();
12832                 if (ch == 'e' && this.isExpOperator(peekCh)) {
12833                   number += ch;
12834                 } else if (this.isExpOperator(ch) &&
12835                     peekCh && this.isNumber(peekCh) &&
12836                     number.charAt(number.length - 1) == 'e') {
12837                   number += ch;
12838                 } else if (this.isExpOperator(ch) &&
12839                     (!peekCh || !this.isNumber(peekCh)) &&
12840                     number.charAt(number.length - 1) == 'e') {
12841                   this.throwError('Invalid exponent');
12842                 } else {
12843                   break;
12844                 }
12845               }
12846               this.index++;
12847             }
12848             this.tokens.push({
12849               index: start,
12850               text: number,
12851               constant: true,
12852               value: Number(number)
12853             });
12854           },
12855
12856           readIdent: function() {
12857             var start = this.index;
12858             while (this.index < this.text.length) {
12859               var ch = this.text.charAt(this.index);
12860               if (!(this.isIdent(ch) || this.isNumber(ch))) {
12861                 break;
12862               }
12863               this.index++;
12864             }
12865             this.tokens.push({
12866               index: start,
12867               text: this.text.slice(start, this.index),
12868               identifier: true
12869             });
12870           },
12871
12872           readString: function(quote) {
12873             var start = this.index;
12874             this.index++;
12875             var string = '';
12876             var rawString = quote;
12877             var escape = false;
12878             while (this.index < this.text.length) {
12879               var ch = this.text.charAt(this.index);
12880               rawString += ch;
12881               if (escape) {
12882                 if (ch === 'u') {
12883                   var hex = this.text.substring(this.index + 1, this.index + 5);
12884                   if (!hex.match(/[\da-f]{4}/i)) {
12885                     this.throwError('Invalid unicode escape [\\u' + hex + ']');
12886                   }
12887                   this.index += 4;
12888                   string += String.fromCharCode(parseInt(hex, 16));
12889                 } else {
12890                   var rep = ESCAPE[ch];
12891                   string = string + (rep || ch);
12892                 }
12893                 escape = false;
12894               } else if (ch === '\\') {
12895                 escape = true;
12896               } else if (ch === quote) {
12897                 this.index++;
12898                 this.tokens.push({
12899                   index: start,
12900                   text: rawString,
12901                   constant: true,
12902                   value: string
12903                 });
12904                 return;
12905               } else {
12906                 string += ch;
12907               }
12908               this.index++;
12909             }
12910             this.throwError('Unterminated quote', start);
12911           }
12912         };
12913
12914         var AST = function(lexer, options) {
12915           this.lexer = lexer;
12916           this.options = options;
12917         };
12918
12919         AST.Program = 'Program';
12920         AST.ExpressionStatement = 'ExpressionStatement';
12921         AST.AssignmentExpression = 'AssignmentExpression';
12922         AST.ConditionalExpression = 'ConditionalExpression';
12923         AST.LogicalExpression = 'LogicalExpression';
12924         AST.BinaryExpression = 'BinaryExpression';
12925         AST.UnaryExpression = 'UnaryExpression';
12926         AST.CallExpression = 'CallExpression';
12927         AST.MemberExpression = 'MemberExpression';
12928         AST.Identifier = 'Identifier';
12929         AST.Literal = 'Literal';
12930         AST.ArrayExpression = 'ArrayExpression';
12931         AST.Property = 'Property';
12932         AST.ObjectExpression = 'ObjectExpression';
12933         AST.ThisExpression = 'ThisExpression';
12934
12935         // Internal use only
12936         AST.NGValueParameter = 'NGValueParameter';
12937
12938         AST.prototype = {
12939           ast: function(text) {
12940             this.text = text;
12941             this.tokens = this.lexer.lex(text);
12942
12943             var value = this.program();
12944
12945             if (this.tokens.length !== 0) {
12946               this.throwError('is an unexpected token', this.tokens[0]);
12947             }
12948
12949             return value;
12950           },
12951
12952           program: function() {
12953             var body = [];
12954             while (true) {
12955               if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12956                 body.push(this.expressionStatement());
12957               if (!this.expect(';')) {
12958                 return { type: AST.Program, body: body};
12959               }
12960             }
12961           },
12962
12963           expressionStatement: function() {
12964             return { type: AST.ExpressionStatement, expression: this.filterChain() };
12965           },
12966
12967           filterChain: function() {
12968             var left = this.expression();
12969             var token;
12970             while ((token = this.expect('|'))) {
12971               left = this.filter(left);
12972             }
12973             return left;
12974           },
12975
12976           expression: function() {
12977             return this.assignment();
12978           },
12979
12980           assignment: function() {
12981             var result = this.ternary();
12982             if (this.expect('=')) {
12983               result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12984             }
12985             return result;
12986           },
12987
12988           ternary: function() {
12989             var test = this.logicalOR();
12990             var alternate;
12991             var consequent;
12992             if (this.expect('?')) {
12993               alternate = this.expression();
12994               if (this.consume(':')) {
12995                 consequent = this.expression();
12996                 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12997               }
12998             }
12999             return test;
13000           },
13001
13002           logicalOR: function() {
13003             var left = this.logicalAND();
13004             while (this.expect('||')) {
13005               left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13006             }
13007             return left;
13008           },
13009
13010           logicalAND: function() {
13011             var left = this.equality();
13012             while (this.expect('&&')) {
13013               left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13014             }
13015             return left;
13016           },
13017
13018           equality: function() {
13019             var left = this.relational();
13020             var token;
13021             while ((token = this.expect('==','!=','===','!=='))) {
13022               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13023             }
13024             return left;
13025           },
13026
13027           relational: function() {
13028             var left = this.additive();
13029             var token;
13030             while ((token = this.expect('<', '>', '<=', '>='))) {
13031               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13032             }
13033             return left;
13034           },
13035
13036           additive: function() {
13037             var left = this.multiplicative();
13038             var token;
13039             while ((token = this.expect('+','-'))) {
13040               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13041             }
13042             return left;
13043           },
13044
13045           multiplicative: function() {
13046             var left = this.unary();
13047             var token;
13048             while ((token = this.expect('*','/','%'))) {
13049               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13050             }
13051             return left;
13052           },
13053
13054           unary: function() {
13055             var token;
13056             if ((token = this.expect('+', '-', '!'))) {
13057               return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13058             } else {
13059               return this.primary();
13060             }
13061           },
13062
13063           primary: function() {
13064             var primary;
13065             if (this.expect('(')) {
13066               primary = this.filterChain();
13067               this.consume(')');
13068             } else if (this.expect('[')) {
13069               primary = this.arrayDeclaration();
13070             } else if (this.expect('{')) {
13071               primary = this.object();
13072             } else if (this.constants.hasOwnProperty(this.peek().text)) {
13073               primary = copy(this.constants[this.consume().text]);
13074             } else if (this.peek().identifier) {
13075               primary = this.identifier();
13076             } else if (this.peek().constant) {
13077               primary = this.constant();
13078             } else {
13079               this.throwError('not a primary expression', this.peek());
13080             }
13081
13082             var next;
13083             while ((next = this.expect('(', '[', '.'))) {
13084               if (next.text === '(') {
13085                 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13086                 this.consume(')');
13087               } else if (next.text === '[') {
13088                 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13089                 this.consume(']');
13090               } else if (next.text === '.') {
13091                 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13092               } else {
13093                 this.throwError('IMPOSSIBLE');
13094               }
13095             }
13096             return primary;
13097           },
13098
13099           filter: function(baseExpression) {
13100             var args = [baseExpression];
13101             var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13102
13103             while (this.expect(':')) {
13104               args.push(this.expression());
13105             }
13106
13107             return result;
13108           },
13109
13110           parseArguments: function() {
13111             var args = [];
13112             if (this.peekToken().text !== ')') {
13113               do {
13114                 args.push(this.expression());
13115               } while (this.expect(','));
13116             }
13117             return args;
13118           },
13119
13120           identifier: function() {
13121             var token = this.consume();
13122             if (!token.identifier) {
13123               this.throwError('is not a valid identifier', token);
13124             }
13125             return { type: AST.Identifier, name: token.text };
13126           },
13127
13128           constant: function() {
13129             // TODO check that it is a constant
13130             return { type: AST.Literal, value: this.consume().value };
13131           },
13132
13133           arrayDeclaration: function() {
13134             var elements = [];
13135             if (this.peekToken().text !== ']') {
13136               do {
13137                 if (this.peek(']')) {
13138                   // Support trailing commas per ES5.1.
13139                   break;
13140                 }
13141                 elements.push(this.expression());
13142               } while (this.expect(','));
13143             }
13144             this.consume(']');
13145
13146             return { type: AST.ArrayExpression, elements: elements };
13147           },
13148
13149           object: function() {
13150             var properties = [], property;
13151             if (this.peekToken().text !== '}') {
13152               do {
13153                 if (this.peek('}')) {
13154                   // Support trailing commas per ES5.1.
13155                   break;
13156                 }
13157                 property = {type: AST.Property, kind: 'init'};
13158                 if (this.peek().constant) {
13159                   property.key = this.constant();
13160                 } else if (this.peek().identifier) {
13161                   property.key = this.identifier();
13162                 } else {
13163                   this.throwError("invalid key", this.peek());
13164                 }
13165                 this.consume(':');
13166                 property.value = this.expression();
13167                 properties.push(property);
13168               } while (this.expect(','));
13169             }
13170             this.consume('}');
13171
13172             return {type: AST.ObjectExpression, properties: properties };
13173           },
13174
13175           throwError: function(msg, token) {
13176             throw $parseMinErr('syntax',
13177                 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13178                   token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13179           },
13180
13181           consume: function(e1) {
13182             if (this.tokens.length === 0) {
13183               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13184             }
13185
13186             var token = this.expect(e1);
13187             if (!token) {
13188               this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13189             }
13190             return token;
13191           },
13192
13193           peekToken: function() {
13194             if (this.tokens.length === 0) {
13195               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13196             }
13197             return this.tokens[0];
13198           },
13199
13200           peek: function(e1, e2, e3, e4) {
13201             return this.peekAhead(0, e1, e2, e3, e4);
13202           },
13203
13204           peekAhead: function(i, e1, e2, e3, e4) {
13205             if (this.tokens.length > i) {
13206               var token = this.tokens[i];
13207               var t = token.text;
13208               if (t === e1 || t === e2 || t === e3 || t === e4 ||
13209                   (!e1 && !e2 && !e3 && !e4)) {
13210                 return token;
13211               }
13212             }
13213             return false;
13214           },
13215
13216           expect: function(e1, e2, e3, e4) {
13217             var token = this.peek(e1, e2, e3, e4);
13218             if (token) {
13219               this.tokens.shift();
13220               return token;
13221             }
13222             return false;
13223           },
13224
13225
13226           /* `undefined` is not a constant, it is an identifier,
13227            * but using it as an identifier is not supported
13228            */
13229           constants: {
13230             'true': { type: AST.Literal, value: true },
13231             'false': { type: AST.Literal, value: false },
13232             'null': { type: AST.Literal, value: null },
13233             'undefined': {type: AST.Literal, value: undefined },
13234             'this': {type: AST.ThisExpression }
13235           }
13236         };
13237
13238         function ifDefined(v, d) {
13239           return typeof v !== 'undefined' ? v : d;
13240         }
13241
13242         function plusFn(l, r) {
13243           if (typeof l === 'undefined') return r;
13244           if (typeof r === 'undefined') return l;
13245           return l + r;
13246         }
13247
13248         function isStateless($filter, filterName) {
13249           var fn = $filter(filterName);
13250           return !fn.$stateful;
13251         }
13252
13253         function findConstantAndWatchExpressions(ast, $filter) {
13254           var allConstants;
13255           var argsToWatch;
13256           switch (ast.type) {
13257           case AST.Program:
13258             allConstants = true;
13259             forEach(ast.body, function(expr) {
13260               findConstantAndWatchExpressions(expr.expression, $filter);
13261               allConstants = allConstants && expr.expression.constant;
13262             });
13263             ast.constant = allConstants;
13264             break;
13265           case AST.Literal:
13266             ast.constant = true;
13267             ast.toWatch = [];
13268             break;
13269           case AST.UnaryExpression:
13270             findConstantAndWatchExpressions(ast.argument, $filter);
13271             ast.constant = ast.argument.constant;
13272             ast.toWatch = ast.argument.toWatch;
13273             break;
13274           case AST.BinaryExpression:
13275             findConstantAndWatchExpressions(ast.left, $filter);
13276             findConstantAndWatchExpressions(ast.right, $filter);
13277             ast.constant = ast.left.constant && ast.right.constant;
13278             ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13279             break;
13280           case AST.LogicalExpression:
13281             findConstantAndWatchExpressions(ast.left, $filter);
13282             findConstantAndWatchExpressions(ast.right, $filter);
13283             ast.constant = ast.left.constant && ast.right.constant;
13284             ast.toWatch = ast.constant ? [] : [ast];
13285             break;
13286           case AST.ConditionalExpression:
13287             findConstantAndWatchExpressions(ast.test, $filter);
13288             findConstantAndWatchExpressions(ast.alternate, $filter);
13289             findConstantAndWatchExpressions(ast.consequent, $filter);
13290             ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13291             ast.toWatch = ast.constant ? [] : [ast];
13292             break;
13293           case AST.Identifier:
13294             ast.constant = false;
13295             ast.toWatch = [ast];
13296             break;
13297           case AST.MemberExpression:
13298             findConstantAndWatchExpressions(ast.object, $filter);
13299             if (ast.computed) {
13300               findConstantAndWatchExpressions(ast.property, $filter);
13301             }
13302             ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13303             ast.toWatch = [ast];
13304             break;
13305           case AST.CallExpression:
13306             allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13307             argsToWatch = [];
13308             forEach(ast.arguments, function(expr) {
13309               findConstantAndWatchExpressions(expr, $filter);
13310               allConstants = allConstants && expr.constant;
13311               if (!expr.constant) {
13312                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13313               }
13314             });
13315             ast.constant = allConstants;
13316             ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13317             break;
13318           case AST.AssignmentExpression:
13319             findConstantAndWatchExpressions(ast.left, $filter);
13320             findConstantAndWatchExpressions(ast.right, $filter);
13321             ast.constant = ast.left.constant && ast.right.constant;
13322             ast.toWatch = [ast];
13323             break;
13324           case AST.ArrayExpression:
13325             allConstants = true;
13326             argsToWatch = [];
13327             forEach(ast.elements, function(expr) {
13328               findConstantAndWatchExpressions(expr, $filter);
13329               allConstants = allConstants && expr.constant;
13330               if (!expr.constant) {
13331                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13332               }
13333             });
13334             ast.constant = allConstants;
13335             ast.toWatch = argsToWatch;
13336             break;
13337           case AST.ObjectExpression:
13338             allConstants = true;
13339             argsToWatch = [];
13340             forEach(ast.properties, function(property) {
13341               findConstantAndWatchExpressions(property.value, $filter);
13342               allConstants = allConstants && property.value.constant;
13343               if (!property.value.constant) {
13344                 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13345               }
13346             });
13347             ast.constant = allConstants;
13348             ast.toWatch = argsToWatch;
13349             break;
13350           case AST.ThisExpression:
13351             ast.constant = false;
13352             ast.toWatch = [];
13353             break;
13354           }
13355         }
13356
13357         function getInputs(body) {
13358           if (body.length != 1) return;
13359           var lastExpression = body[0].expression;
13360           var candidate = lastExpression.toWatch;
13361           if (candidate.length !== 1) return candidate;
13362           return candidate[0] !== lastExpression ? candidate : undefined;
13363         }
13364
13365         function isAssignable(ast) {
13366           return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13367         }
13368
13369         function assignableAST(ast) {
13370           if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13371             return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13372           }
13373         }
13374
13375         function isLiteral(ast) {
13376           return ast.body.length === 0 ||
13377               ast.body.length === 1 && (
13378               ast.body[0].expression.type === AST.Literal ||
13379               ast.body[0].expression.type === AST.ArrayExpression ||
13380               ast.body[0].expression.type === AST.ObjectExpression);
13381         }
13382
13383         function isConstant(ast) {
13384           return ast.constant;
13385         }
13386
13387         function ASTCompiler(astBuilder, $filter) {
13388           this.astBuilder = astBuilder;
13389           this.$filter = $filter;
13390         }
13391
13392         ASTCompiler.prototype = {
13393           compile: function(expression, expensiveChecks) {
13394             var self = this;
13395             var ast = this.astBuilder.ast(expression);
13396             this.state = {
13397               nextId: 0,
13398               filters: {},
13399               expensiveChecks: expensiveChecks,
13400               fn: {vars: [], body: [], own: {}},
13401               assign: {vars: [], body: [], own: {}},
13402               inputs: []
13403             };
13404             findConstantAndWatchExpressions(ast, self.$filter);
13405             var extra = '';
13406             var assignable;
13407             this.stage = 'assign';
13408             if ((assignable = assignableAST(ast))) {
13409               this.state.computing = 'assign';
13410               var result = this.nextId();
13411               this.recurse(assignable, result);
13412               this.return_(result);
13413               extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13414             }
13415             var toWatch = getInputs(ast.body);
13416             self.stage = 'inputs';
13417             forEach(toWatch, function(watch, key) {
13418               var fnKey = 'fn' + key;
13419               self.state[fnKey] = {vars: [], body: [], own: {}};
13420               self.state.computing = fnKey;
13421               var intoId = self.nextId();
13422               self.recurse(watch, intoId);
13423               self.return_(intoId);
13424               self.state.inputs.push(fnKey);
13425               watch.watchId = key;
13426             });
13427             this.state.computing = 'fn';
13428             this.stage = 'main';
13429             this.recurse(ast);
13430             var fnString =
13431               // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13432               // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13433               '"' + this.USE + ' ' + this.STRICT + '";\n' +
13434               this.filterPrefix() +
13435               'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13436               extra +
13437               this.watchFns() +
13438               'return fn;';
13439
13440             /* jshint -W054 */
13441             var fn = (new Function('$filter',
13442                 'ensureSafeMemberName',
13443                 'ensureSafeObject',
13444                 'ensureSafeFunction',
13445                 'getStringValue',
13446                 'ensureSafeAssignContext',
13447                 'ifDefined',
13448                 'plus',
13449                 'text',
13450                 fnString))(
13451                   this.$filter,
13452                   ensureSafeMemberName,
13453                   ensureSafeObject,
13454                   ensureSafeFunction,
13455                   getStringValue,
13456                   ensureSafeAssignContext,
13457                   ifDefined,
13458                   plusFn,
13459                   expression);
13460             /* jshint +W054 */
13461             this.state = this.stage = undefined;
13462             fn.literal = isLiteral(ast);
13463             fn.constant = isConstant(ast);
13464             return fn;
13465           },
13466
13467           USE: 'use',
13468
13469           STRICT: 'strict',
13470
13471           watchFns: function() {
13472             var result = [];
13473             var fns = this.state.inputs;
13474             var self = this;
13475             forEach(fns, function(name) {
13476               result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13477             });
13478             if (fns.length) {
13479               result.push('fn.inputs=[' + fns.join(',') + '];');
13480             }
13481             return result.join('');
13482           },
13483
13484           generateFunction: function(name, params) {
13485             return 'function(' + params + '){' +
13486                 this.varsPrefix(name) +
13487                 this.body(name) +
13488                 '};';
13489           },
13490
13491           filterPrefix: function() {
13492             var parts = [];
13493             var self = this;
13494             forEach(this.state.filters, function(id, filter) {
13495               parts.push(id + '=$filter(' + self.escape(filter) + ')');
13496             });
13497             if (parts.length) return 'var ' + parts.join(',') + ';';
13498             return '';
13499           },
13500
13501           varsPrefix: function(section) {
13502             return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13503           },
13504
13505           body: function(section) {
13506             return this.state[section].body.join('');
13507           },
13508
13509           recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13510             var left, right, self = this, args, expression;
13511             recursionFn = recursionFn || noop;
13512             if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13513               intoId = intoId || this.nextId();
13514               this.if_('i',
13515                 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13516                 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13517               );
13518               return;
13519             }
13520             switch (ast.type) {
13521             case AST.Program:
13522               forEach(ast.body, function(expression, pos) {
13523                 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13524                 if (pos !== ast.body.length - 1) {
13525                   self.current().body.push(right, ';');
13526                 } else {
13527                   self.return_(right);
13528                 }
13529               });
13530               break;
13531             case AST.Literal:
13532               expression = this.escape(ast.value);
13533               this.assign(intoId, expression);
13534               recursionFn(expression);
13535               break;
13536             case AST.UnaryExpression:
13537               this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13538               expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13539               this.assign(intoId, expression);
13540               recursionFn(expression);
13541               break;
13542             case AST.BinaryExpression:
13543               this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13544               this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13545               if (ast.operator === '+') {
13546                 expression = this.plus(left, right);
13547               } else if (ast.operator === '-') {
13548                 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13549               } else {
13550                 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13551               }
13552               this.assign(intoId, expression);
13553               recursionFn(expression);
13554               break;
13555             case AST.LogicalExpression:
13556               intoId = intoId || this.nextId();
13557               self.recurse(ast.left, intoId);
13558               self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13559               recursionFn(intoId);
13560               break;
13561             case AST.ConditionalExpression:
13562               intoId = intoId || this.nextId();
13563               self.recurse(ast.test, intoId);
13564               self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13565               recursionFn(intoId);
13566               break;
13567             case AST.Identifier:
13568               intoId = intoId || this.nextId();
13569               if (nameId) {
13570                 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13571                 nameId.computed = false;
13572                 nameId.name = ast.name;
13573               }
13574               ensureSafeMemberName(ast.name);
13575               self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13576                 function() {
13577                   self.if_(self.stage === 'inputs' || 's', function() {
13578                     if (create && create !== 1) {
13579                       self.if_(
13580                         self.not(self.nonComputedMember('s', ast.name)),
13581                         self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13582                     }
13583                     self.assign(intoId, self.nonComputedMember('s', ast.name));
13584                   });
13585                 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13586                 );
13587               if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13588                 self.addEnsureSafeObject(intoId);
13589               }
13590               recursionFn(intoId);
13591               break;
13592             case AST.MemberExpression:
13593               left = nameId && (nameId.context = this.nextId()) || this.nextId();
13594               intoId = intoId || this.nextId();
13595               self.recurse(ast.object, left, undefined, function() {
13596                 self.if_(self.notNull(left), function() {
13597                   if (ast.computed) {
13598                     right = self.nextId();
13599                     self.recurse(ast.property, right);
13600                     self.getStringValue(right);
13601                     self.addEnsureSafeMemberName(right);
13602                     if (create && create !== 1) {
13603                       self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13604                     }
13605                     expression = self.ensureSafeObject(self.computedMember(left, right));
13606                     self.assign(intoId, expression);
13607                     if (nameId) {
13608                       nameId.computed = true;
13609                       nameId.name = right;
13610                     }
13611                   } else {
13612                     ensureSafeMemberName(ast.property.name);
13613                     if (create && create !== 1) {
13614                       self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13615                     }
13616                     expression = self.nonComputedMember(left, ast.property.name);
13617                     if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13618                       expression = self.ensureSafeObject(expression);
13619                     }
13620                     self.assign(intoId, expression);
13621                     if (nameId) {
13622                       nameId.computed = false;
13623                       nameId.name = ast.property.name;
13624                     }
13625                   }
13626                 }, function() {
13627                   self.assign(intoId, 'undefined');
13628                 });
13629                 recursionFn(intoId);
13630               }, !!create);
13631               break;
13632             case AST.CallExpression:
13633               intoId = intoId || this.nextId();
13634               if (ast.filter) {
13635                 right = self.filter(ast.callee.name);
13636                 args = [];
13637                 forEach(ast.arguments, function(expr) {
13638                   var argument = self.nextId();
13639                   self.recurse(expr, argument);
13640                   args.push(argument);
13641                 });
13642                 expression = right + '(' + args.join(',') + ')';
13643                 self.assign(intoId, expression);
13644                 recursionFn(intoId);
13645               } else {
13646                 right = self.nextId();
13647                 left = {};
13648                 args = [];
13649                 self.recurse(ast.callee, right, left, function() {
13650                   self.if_(self.notNull(right), function() {
13651                     self.addEnsureSafeFunction(right);
13652                     forEach(ast.arguments, function(expr) {
13653                       self.recurse(expr, self.nextId(), undefined, function(argument) {
13654                         args.push(self.ensureSafeObject(argument));
13655                       });
13656                     });
13657                     if (left.name) {
13658                       if (!self.state.expensiveChecks) {
13659                         self.addEnsureSafeObject(left.context);
13660                       }
13661                       expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13662                     } else {
13663                       expression = right + '(' + args.join(',') + ')';
13664                     }
13665                     expression = self.ensureSafeObject(expression);
13666                     self.assign(intoId, expression);
13667                   }, function() {
13668                     self.assign(intoId, 'undefined');
13669                   });
13670                   recursionFn(intoId);
13671                 });
13672               }
13673               break;
13674             case AST.AssignmentExpression:
13675               right = this.nextId();
13676               left = {};
13677               if (!isAssignable(ast.left)) {
13678                 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13679               }
13680               this.recurse(ast.left, undefined, left, function() {
13681                 self.if_(self.notNull(left.context), function() {
13682                   self.recurse(ast.right, right);
13683                   self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13684                   self.addEnsureSafeAssignContext(left.context);
13685                   expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13686                   self.assign(intoId, expression);
13687                   recursionFn(intoId || expression);
13688                 });
13689               }, 1);
13690               break;
13691             case AST.ArrayExpression:
13692               args = [];
13693               forEach(ast.elements, function(expr) {
13694                 self.recurse(expr, self.nextId(), undefined, function(argument) {
13695                   args.push(argument);
13696                 });
13697               });
13698               expression = '[' + args.join(',') + ']';
13699               this.assign(intoId, expression);
13700               recursionFn(expression);
13701               break;
13702             case AST.ObjectExpression:
13703               args = [];
13704               forEach(ast.properties, function(property) {
13705                 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13706                   args.push(self.escape(
13707                       property.key.type === AST.Identifier ? property.key.name :
13708                         ('' + property.key.value)) +
13709                       ':' + expr);
13710                 });
13711               });
13712               expression = '{' + args.join(',') + '}';
13713               this.assign(intoId, expression);
13714               recursionFn(expression);
13715               break;
13716             case AST.ThisExpression:
13717               this.assign(intoId, 's');
13718               recursionFn('s');
13719               break;
13720             case AST.NGValueParameter:
13721               this.assign(intoId, 'v');
13722               recursionFn('v');
13723               break;
13724             }
13725           },
13726
13727           getHasOwnProperty: function(element, property) {
13728             var key = element + '.' + property;
13729             var own = this.current().own;
13730             if (!own.hasOwnProperty(key)) {
13731               own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13732             }
13733             return own[key];
13734           },
13735
13736           assign: function(id, value) {
13737             if (!id) return;
13738             this.current().body.push(id, '=', value, ';');
13739             return id;
13740           },
13741
13742           filter: function(filterName) {
13743             if (!this.state.filters.hasOwnProperty(filterName)) {
13744               this.state.filters[filterName] = this.nextId(true);
13745             }
13746             return this.state.filters[filterName];
13747           },
13748
13749           ifDefined: function(id, defaultValue) {
13750             return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13751           },
13752
13753           plus: function(left, right) {
13754             return 'plus(' + left + ',' + right + ')';
13755           },
13756
13757           return_: function(id) {
13758             this.current().body.push('return ', id, ';');
13759           },
13760
13761           if_: function(test, alternate, consequent) {
13762             if (test === true) {
13763               alternate();
13764             } else {
13765               var body = this.current().body;
13766               body.push('if(', test, '){');
13767               alternate();
13768               body.push('}');
13769               if (consequent) {
13770                 body.push('else{');
13771                 consequent();
13772                 body.push('}');
13773               }
13774             }
13775           },
13776
13777           not: function(expression) {
13778             return '!(' + expression + ')';
13779           },
13780
13781           notNull: function(expression) {
13782             return expression + '!=null';
13783           },
13784
13785           nonComputedMember: function(left, right) {
13786             return left + '.' + right;
13787           },
13788
13789           computedMember: function(left, right) {
13790             return left + '[' + right + ']';
13791           },
13792
13793           member: function(left, right, computed) {
13794             if (computed) return this.computedMember(left, right);
13795             return this.nonComputedMember(left, right);
13796           },
13797
13798           addEnsureSafeObject: function(item) {
13799             this.current().body.push(this.ensureSafeObject(item), ';');
13800           },
13801
13802           addEnsureSafeMemberName: function(item) {
13803             this.current().body.push(this.ensureSafeMemberName(item), ';');
13804           },
13805
13806           addEnsureSafeFunction: function(item) {
13807             this.current().body.push(this.ensureSafeFunction(item), ';');
13808           },
13809
13810           addEnsureSafeAssignContext: function(item) {
13811             this.current().body.push(this.ensureSafeAssignContext(item), ';');
13812           },
13813
13814           ensureSafeObject: function(item) {
13815             return 'ensureSafeObject(' + item + ',text)';
13816           },
13817
13818           ensureSafeMemberName: function(item) {
13819             return 'ensureSafeMemberName(' + item + ',text)';
13820           },
13821
13822           ensureSafeFunction: function(item) {
13823             return 'ensureSafeFunction(' + item + ',text)';
13824           },
13825
13826           getStringValue: function(item) {
13827             this.assign(item, 'getStringValue(' + item + ',text)');
13828           },
13829
13830           ensureSafeAssignContext: function(item) {
13831             return 'ensureSafeAssignContext(' + item + ',text)';
13832           },
13833
13834           lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13835             var self = this;
13836             return function() {
13837               self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13838             };
13839           },
13840
13841           lazyAssign: function(id, value) {
13842             var self = this;
13843             return function() {
13844               self.assign(id, value);
13845             };
13846           },
13847
13848           stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13849
13850           stringEscapeFn: function(c) {
13851             return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13852           },
13853
13854           escape: function(value) {
13855             if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13856             if (isNumber(value)) return value.toString();
13857             if (value === true) return 'true';
13858             if (value === false) return 'false';
13859             if (value === null) return 'null';
13860             if (typeof value === 'undefined') return 'undefined';
13861
13862             throw $parseMinErr('esc', 'IMPOSSIBLE');
13863           },
13864
13865           nextId: function(skip, init) {
13866             var id = 'v' + (this.state.nextId++);
13867             if (!skip) {
13868               this.current().vars.push(id + (init ? '=' + init : ''));
13869             }
13870             return id;
13871           },
13872
13873           current: function() {
13874             return this.state[this.state.computing];
13875           }
13876         };
13877
13878
13879         function ASTInterpreter(astBuilder, $filter) {
13880           this.astBuilder = astBuilder;
13881           this.$filter = $filter;
13882         }
13883
13884         ASTInterpreter.prototype = {
13885           compile: function(expression, expensiveChecks) {
13886             var self = this;
13887             var ast = this.astBuilder.ast(expression);
13888             this.expression = expression;
13889             this.expensiveChecks = expensiveChecks;
13890             findConstantAndWatchExpressions(ast, self.$filter);
13891             var assignable;
13892             var assign;
13893             if ((assignable = assignableAST(ast))) {
13894               assign = this.recurse(assignable);
13895             }
13896             var toWatch = getInputs(ast.body);
13897             var inputs;
13898             if (toWatch) {
13899               inputs = [];
13900               forEach(toWatch, function(watch, key) {
13901                 var input = self.recurse(watch);
13902                 watch.input = input;
13903                 inputs.push(input);
13904                 watch.watchId = key;
13905               });
13906             }
13907             var expressions = [];
13908             forEach(ast.body, function(expression) {
13909               expressions.push(self.recurse(expression.expression));
13910             });
13911             var fn = ast.body.length === 0 ? function() {} :
13912                      ast.body.length === 1 ? expressions[0] :
13913                      function(scope, locals) {
13914                        var lastValue;
13915                        forEach(expressions, function(exp) {
13916                          lastValue = exp(scope, locals);
13917                        });
13918                        return lastValue;
13919                      };
13920             if (assign) {
13921               fn.assign = function(scope, value, locals) {
13922                 return assign(scope, locals, value);
13923               };
13924             }
13925             if (inputs) {
13926               fn.inputs = inputs;
13927             }
13928             fn.literal = isLiteral(ast);
13929             fn.constant = isConstant(ast);
13930             return fn;
13931           },
13932
13933           recurse: function(ast, context, create) {
13934             var left, right, self = this, args, expression;
13935             if (ast.input) {
13936               return this.inputs(ast.input, ast.watchId);
13937             }
13938             switch (ast.type) {
13939             case AST.Literal:
13940               return this.value(ast.value, context);
13941             case AST.UnaryExpression:
13942               right = this.recurse(ast.argument);
13943               return this['unary' + ast.operator](right, context);
13944             case AST.BinaryExpression:
13945               left = this.recurse(ast.left);
13946               right = this.recurse(ast.right);
13947               return this['binary' + ast.operator](left, right, context);
13948             case AST.LogicalExpression:
13949               left = this.recurse(ast.left);
13950               right = this.recurse(ast.right);
13951               return this['binary' + ast.operator](left, right, context);
13952             case AST.ConditionalExpression:
13953               return this['ternary?:'](
13954                 this.recurse(ast.test),
13955                 this.recurse(ast.alternate),
13956                 this.recurse(ast.consequent),
13957                 context
13958               );
13959             case AST.Identifier:
13960               ensureSafeMemberName(ast.name, self.expression);
13961               return self.identifier(ast.name,
13962                                      self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13963                                      context, create, self.expression);
13964             case AST.MemberExpression:
13965               left = this.recurse(ast.object, false, !!create);
13966               if (!ast.computed) {
13967                 ensureSafeMemberName(ast.property.name, self.expression);
13968                 right = ast.property.name;
13969               }
13970               if (ast.computed) right = this.recurse(ast.property);
13971               return ast.computed ?
13972                 this.computedMember(left, right, context, create, self.expression) :
13973                 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13974             case AST.CallExpression:
13975               args = [];
13976               forEach(ast.arguments, function(expr) {
13977                 args.push(self.recurse(expr));
13978               });
13979               if (ast.filter) right = this.$filter(ast.callee.name);
13980               if (!ast.filter) right = this.recurse(ast.callee, true);
13981               return ast.filter ?
13982                 function(scope, locals, assign, inputs) {
13983                   var values = [];
13984                   for (var i = 0; i < args.length; ++i) {
13985                     values.push(args[i](scope, locals, assign, inputs));
13986                   }
13987                   var value = right.apply(undefined, values, inputs);
13988                   return context ? {context: undefined, name: undefined, value: value} : value;
13989                 } :
13990                 function(scope, locals, assign, inputs) {
13991                   var rhs = right(scope, locals, assign, inputs);
13992                   var value;
13993                   if (rhs.value != null) {
13994                     ensureSafeObject(rhs.context, self.expression);
13995                     ensureSafeFunction(rhs.value, self.expression);
13996                     var values = [];
13997                     for (var i = 0; i < args.length; ++i) {
13998                       values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13999                     }
14000                     value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14001                   }
14002                   return context ? {value: value} : value;
14003                 };
14004             case AST.AssignmentExpression:
14005               left = this.recurse(ast.left, true, 1);
14006               right = this.recurse(ast.right);
14007               return function(scope, locals, assign, inputs) {
14008                 var lhs = left(scope, locals, assign, inputs);
14009                 var rhs = right(scope, locals, assign, inputs);
14010                 ensureSafeObject(lhs.value, self.expression);
14011                 ensureSafeAssignContext(lhs.context);
14012                 lhs.context[lhs.name] = rhs;
14013                 return context ? {value: rhs} : rhs;
14014               };
14015             case AST.ArrayExpression:
14016               args = [];
14017               forEach(ast.elements, function(expr) {
14018                 args.push(self.recurse(expr));
14019               });
14020               return function(scope, locals, assign, inputs) {
14021                 var value = [];
14022                 for (var i = 0; i < args.length; ++i) {
14023                   value.push(args[i](scope, locals, assign, inputs));
14024                 }
14025                 return context ? {value: value} : value;
14026               };
14027             case AST.ObjectExpression:
14028               args = [];
14029               forEach(ast.properties, function(property) {
14030                 args.push({key: property.key.type === AST.Identifier ?
14031                                 property.key.name :
14032                                 ('' + property.key.value),
14033                            value: self.recurse(property.value)
14034                 });
14035               });
14036               return function(scope, locals, assign, inputs) {
14037                 var value = {};
14038                 for (var i = 0; i < args.length; ++i) {
14039                   value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14040                 }
14041                 return context ? {value: value} : value;
14042               };
14043             case AST.ThisExpression:
14044               return function(scope) {
14045                 return context ? {value: scope} : scope;
14046               };
14047             case AST.NGValueParameter:
14048               return function(scope, locals, assign, inputs) {
14049                 return context ? {value: assign} : assign;
14050               };
14051             }
14052           },
14053
14054           'unary+': function(argument, context) {
14055             return function(scope, locals, assign, inputs) {
14056               var arg = argument(scope, locals, assign, inputs);
14057               if (isDefined(arg)) {
14058                 arg = +arg;
14059               } else {
14060                 arg = 0;
14061               }
14062               return context ? {value: arg} : arg;
14063             };
14064           },
14065           'unary-': function(argument, context) {
14066             return function(scope, locals, assign, inputs) {
14067               var arg = argument(scope, locals, assign, inputs);
14068               if (isDefined(arg)) {
14069                 arg = -arg;
14070               } else {
14071                 arg = 0;
14072               }
14073               return context ? {value: arg} : arg;
14074             };
14075           },
14076           'unary!': function(argument, context) {
14077             return function(scope, locals, assign, inputs) {
14078               var arg = !argument(scope, locals, assign, inputs);
14079               return context ? {value: arg} : arg;
14080             };
14081           },
14082           'binary+': function(left, right, context) {
14083             return function(scope, locals, assign, inputs) {
14084               var lhs = left(scope, locals, assign, inputs);
14085               var rhs = right(scope, locals, assign, inputs);
14086               var arg = plusFn(lhs, rhs);
14087               return context ? {value: arg} : arg;
14088             };
14089           },
14090           'binary-': function(left, right, context) {
14091             return function(scope, locals, assign, inputs) {
14092               var lhs = left(scope, locals, assign, inputs);
14093               var rhs = right(scope, locals, assign, inputs);
14094               var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14095               return context ? {value: arg} : arg;
14096             };
14097           },
14098           'binary*': function(left, right, context) {
14099             return function(scope, locals, assign, inputs) {
14100               var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14101               return context ? {value: arg} : arg;
14102             };
14103           },
14104           'binary/': function(left, right, context) {
14105             return function(scope, locals, assign, inputs) {
14106               var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14107               return context ? {value: arg} : arg;
14108             };
14109           },
14110           'binary%': function(left, right, context) {
14111             return function(scope, locals, assign, inputs) {
14112               var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14113               return context ? {value: arg} : arg;
14114             };
14115           },
14116           'binary===': function(left, right, context) {
14117             return function(scope, locals, assign, inputs) {
14118               var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14119               return context ? {value: arg} : arg;
14120             };
14121           },
14122           'binary!==': function(left, right, context) {
14123             return function(scope, locals, assign, inputs) {
14124               var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14125               return context ? {value: arg} : arg;
14126             };
14127           },
14128           'binary==': function(left, right, context) {
14129             return function(scope, locals, assign, inputs) {
14130               var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14131               return context ? {value: arg} : arg;
14132             };
14133           },
14134           'binary!=': function(left, right, context) {
14135             return function(scope, locals, assign, inputs) {
14136               var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14137               return context ? {value: arg} : arg;
14138             };
14139           },
14140           'binary<': function(left, right, context) {
14141             return function(scope, locals, assign, inputs) {
14142               var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14143               return context ? {value: arg} : arg;
14144             };
14145           },
14146           'binary>': function(left, right, context) {
14147             return function(scope, locals, assign, inputs) {
14148               var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14149               return context ? {value: arg} : arg;
14150             };
14151           },
14152           'binary<=': function(left, right, context) {
14153             return function(scope, locals, assign, inputs) {
14154               var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14155               return context ? {value: arg} : arg;
14156             };
14157           },
14158           'binary>=': function(left, right, context) {
14159             return function(scope, locals, assign, inputs) {
14160               var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14161               return context ? {value: arg} : arg;
14162             };
14163           },
14164           'binary&&': function(left, right, context) {
14165             return function(scope, locals, assign, inputs) {
14166               var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14167               return context ? {value: arg} : arg;
14168             };
14169           },
14170           'binary||': function(left, right, context) {
14171             return function(scope, locals, assign, inputs) {
14172               var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14173               return context ? {value: arg} : arg;
14174             };
14175           },
14176           'ternary?:': function(test, alternate, consequent, context) {
14177             return function(scope, locals, assign, inputs) {
14178               var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14179               return context ? {value: arg} : arg;
14180             };
14181           },
14182           value: function(value, context) {
14183             return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14184           },
14185           identifier: function(name, expensiveChecks, context, create, expression) {
14186             return function(scope, locals, assign, inputs) {
14187               var base = locals && (name in locals) ? locals : scope;
14188               if (create && create !== 1 && base && !(base[name])) {
14189                 base[name] = {};
14190               }
14191               var value = base ? base[name] : undefined;
14192               if (expensiveChecks) {
14193                 ensureSafeObject(value, expression);
14194               }
14195               if (context) {
14196                 return {context: base, name: name, value: value};
14197               } else {
14198                 return value;
14199               }
14200             };
14201           },
14202           computedMember: function(left, right, context, create, expression) {
14203             return function(scope, locals, assign, inputs) {
14204               var lhs = left(scope, locals, assign, inputs);
14205               var rhs;
14206               var value;
14207               if (lhs != null) {
14208                 rhs = right(scope, locals, assign, inputs);
14209                 rhs = getStringValue(rhs);
14210                 ensureSafeMemberName(rhs, expression);
14211                 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14212                   lhs[rhs] = {};
14213                 }
14214                 value = lhs[rhs];
14215                 ensureSafeObject(value, expression);
14216               }
14217               if (context) {
14218                 return {context: lhs, name: rhs, value: value};
14219               } else {
14220                 return value;
14221               }
14222             };
14223           },
14224           nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14225             return function(scope, locals, assign, inputs) {
14226               var lhs = left(scope, locals, assign, inputs);
14227               if (create && create !== 1 && lhs && !(lhs[right])) {
14228                 lhs[right] = {};
14229               }
14230               var value = lhs != null ? lhs[right] : undefined;
14231               if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14232                 ensureSafeObject(value, expression);
14233               }
14234               if (context) {
14235                 return {context: lhs, name: right, value: value};
14236               } else {
14237                 return value;
14238               }
14239             };
14240           },
14241           inputs: function(input, watchId) {
14242             return function(scope, value, locals, inputs) {
14243               if (inputs) return inputs[watchId];
14244               return input(scope, value, locals);
14245             };
14246           }
14247         };
14248
14249         /**
14250          * @constructor
14251          */
14252         var Parser = function(lexer, $filter, options) {
14253           this.lexer = lexer;
14254           this.$filter = $filter;
14255           this.options = options;
14256           this.ast = new AST(this.lexer);
14257           this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14258                                            new ASTCompiler(this.ast, $filter);
14259         };
14260
14261         Parser.prototype = {
14262           constructor: Parser,
14263
14264           parse: function(text) {
14265             return this.astCompiler.compile(text, this.options.expensiveChecks);
14266           }
14267         };
14268
14269         var getterFnCacheDefault = createMap();
14270         var getterFnCacheExpensive = createMap();
14271
14272         function isPossiblyDangerousMemberName(name) {
14273           return name == 'constructor';
14274         }
14275
14276         var objectValueOf = Object.prototype.valueOf;
14277
14278         function getValueOf(value) {
14279           return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14280         }
14281
14282         ///////////////////////////////////
14283
14284         /**
14285          * @ngdoc service
14286          * @name $parse
14287          * @kind function
14288          *
14289          * @description
14290          *
14291          * Converts Angular {@link guide/expression expression} into a function.
14292          *
14293          * ```js
14294          *   var getter = $parse('user.name');
14295          *   var setter = getter.assign;
14296          *   var context = {user:{name:'angular'}};
14297          *   var locals = {user:{name:'local'}};
14298          *
14299          *   expect(getter(context)).toEqual('angular');
14300          *   setter(context, 'newValue');
14301          *   expect(context.user.name).toEqual('newValue');
14302          *   expect(getter(context, locals)).toEqual('local');
14303          * ```
14304          *
14305          *
14306          * @param {string} expression String expression to compile.
14307          * @returns {function(context, locals)} a function which represents the compiled expression:
14308          *
14309          *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14310          *      are evaluated against (typically a scope object).
14311          *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14312          *      `context`.
14313          *
14314          *    The returned function also has the following properties:
14315          *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14316          *        literal.
14317          *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14318          *        constant literals.
14319          *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14320          *        set to a function to change its value on the given context.
14321          *
14322          */
14323
14324
14325         /**
14326          * @ngdoc provider
14327          * @name $parseProvider
14328          *
14329          * @description
14330          * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14331          *  service.
14332          */
14333         function $ParseProvider() {
14334           var cacheDefault = createMap();
14335           var cacheExpensive = createMap();
14336
14337           this.$get = ['$filter', function($filter) {
14338             var noUnsafeEval = csp().noUnsafeEval;
14339             var $parseOptions = {
14340                   csp: noUnsafeEval,
14341                   expensiveChecks: false
14342                 },
14343                 $parseOptionsExpensive = {
14344                   csp: noUnsafeEval,
14345                   expensiveChecks: true
14346                 };
14347
14348             return function $parse(exp, interceptorFn, expensiveChecks) {
14349               var parsedExpression, oneTime, cacheKey;
14350
14351               switch (typeof exp) {
14352                 case 'string':
14353                   exp = exp.trim();
14354                   cacheKey = exp;
14355
14356                   var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14357                   parsedExpression = cache[cacheKey];
14358
14359                   if (!parsedExpression) {
14360                     if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14361                       oneTime = true;
14362                       exp = exp.substring(2);
14363                     }
14364                     var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14365                     var lexer = new Lexer(parseOptions);
14366                     var parser = new Parser(lexer, $filter, parseOptions);
14367                     parsedExpression = parser.parse(exp);
14368                     if (parsedExpression.constant) {
14369                       parsedExpression.$$watchDelegate = constantWatchDelegate;
14370                     } else if (oneTime) {
14371                       parsedExpression.$$watchDelegate = parsedExpression.literal ?
14372                           oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14373                     } else if (parsedExpression.inputs) {
14374                       parsedExpression.$$watchDelegate = inputsWatchDelegate;
14375                     }
14376                     cache[cacheKey] = parsedExpression;
14377                   }
14378                   return addInterceptor(parsedExpression, interceptorFn);
14379
14380                 case 'function':
14381                   return addInterceptor(exp, interceptorFn);
14382
14383                 default:
14384                   return noop;
14385               }
14386             };
14387
14388             function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14389
14390               if (newValue == null || oldValueOfValue == null) { // null/undefined
14391                 return newValue === oldValueOfValue;
14392               }
14393
14394               if (typeof newValue === 'object') {
14395
14396                 // attempt to convert the value to a primitive type
14397                 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14398                 //             be cheaply dirty-checked
14399                 newValue = getValueOf(newValue);
14400
14401                 if (typeof newValue === 'object') {
14402                   // objects/arrays are not supported - deep-watching them would be too expensive
14403                   return false;
14404                 }
14405
14406                 // fall-through to the primitive equality check
14407               }
14408
14409               //Primitive or NaN
14410               return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14411             }
14412
14413             function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14414               var inputExpressions = parsedExpression.inputs;
14415               var lastResult;
14416
14417               if (inputExpressions.length === 1) {
14418                 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14419                 inputExpressions = inputExpressions[0];
14420                 return scope.$watch(function expressionInputWatch(scope) {
14421                   var newInputValue = inputExpressions(scope);
14422                   if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14423                     lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14424                     oldInputValueOf = newInputValue && getValueOf(newInputValue);
14425                   }
14426                   return lastResult;
14427                 }, listener, objectEquality, prettyPrintExpression);
14428               }
14429
14430               var oldInputValueOfValues = [];
14431               var oldInputValues = [];
14432               for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14433                 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14434                 oldInputValues[i] = null;
14435               }
14436
14437               return scope.$watch(function expressionInputsWatch(scope) {
14438                 var changed = false;
14439
14440                 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14441                   var newInputValue = inputExpressions[i](scope);
14442                   if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14443                     oldInputValues[i] = newInputValue;
14444                     oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14445                   }
14446                 }
14447
14448                 if (changed) {
14449                   lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14450                 }
14451
14452                 return lastResult;
14453               }, listener, objectEquality, prettyPrintExpression);
14454             }
14455
14456             function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14457               var unwatch, lastValue;
14458               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14459                 return parsedExpression(scope);
14460               }, function oneTimeListener(value, old, scope) {
14461                 lastValue = value;
14462                 if (isFunction(listener)) {
14463                   listener.apply(this, arguments);
14464                 }
14465                 if (isDefined(value)) {
14466                   scope.$$postDigest(function() {
14467                     if (isDefined(lastValue)) {
14468                       unwatch();
14469                     }
14470                   });
14471                 }
14472               }, objectEquality);
14473             }
14474
14475             function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14476               var unwatch, lastValue;
14477               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14478                 return parsedExpression(scope);
14479               }, function oneTimeListener(value, old, scope) {
14480                 lastValue = value;
14481                 if (isFunction(listener)) {
14482                   listener.call(this, value, old, scope);
14483                 }
14484                 if (isAllDefined(value)) {
14485                   scope.$$postDigest(function() {
14486                     if (isAllDefined(lastValue)) unwatch();
14487                   });
14488                 }
14489               }, objectEquality);
14490
14491               function isAllDefined(value) {
14492                 var allDefined = true;
14493                 forEach(value, function(val) {
14494                   if (!isDefined(val)) allDefined = false;
14495                 });
14496                 return allDefined;
14497               }
14498             }
14499
14500             function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14501               var unwatch;
14502               return unwatch = scope.$watch(function constantWatch(scope) {
14503                 return parsedExpression(scope);
14504               }, function constantListener(value, old, scope) {
14505                 if (isFunction(listener)) {
14506                   listener.apply(this, arguments);
14507                 }
14508                 unwatch();
14509               }, objectEquality);
14510             }
14511
14512             function addInterceptor(parsedExpression, interceptorFn) {
14513               if (!interceptorFn) return parsedExpression;
14514               var watchDelegate = parsedExpression.$$watchDelegate;
14515               var useInputs = false;
14516
14517               var regularWatch =
14518                   watchDelegate !== oneTimeLiteralWatchDelegate &&
14519                   watchDelegate !== oneTimeWatchDelegate;
14520
14521               var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14522                 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14523                 return interceptorFn(value, scope, locals);
14524               } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14525                 var value = parsedExpression(scope, locals, assign, inputs);
14526                 var result = interceptorFn(value, scope, locals);
14527                 // we only return the interceptor's result if the
14528                 // initial value is defined (for bind-once)
14529                 return isDefined(value) ? result : value;
14530               };
14531
14532               // Propagate $$watchDelegates other then inputsWatchDelegate
14533               if (parsedExpression.$$watchDelegate &&
14534                   parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14535                 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14536               } else if (!interceptorFn.$stateful) {
14537                 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14538                 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14539                 fn.$$watchDelegate = inputsWatchDelegate;
14540                 useInputs = !parsedExpression.inputs;
14541                 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14542               }
14543
14544               return fn;
14545             }
14546           }];
14547         }
14548
14549         /**
14550          * @ngdoc service
14551          * @name $q
14552          * @requires $rootScope
14553          *
14554          * @description
14555          * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14556          * when they are done processing.
14557          *
14558          * This is an implementation of promises/deferred objects inspired by
14559          * [Kris Kowal's Q](https://github.com/kriskowal/q).
14560          *
14561          * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14562          * implementations, and the other which resembles ES6 promises to some degree.
14563          *
14564          * # $q constructor
14565          *
14566          * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14567          * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14568          * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14569          *
14570          * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14571          * available yet.
14572          *
14573          * It can be used like so:
14574          *
14575          * ```js
14576          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14577          *   // are available in the current lexical scope (they could have been injected or passed in).
14578          *
14579          *   function asyncGreet(name) {
14580          *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14581          *     return $q(function(resolve, reject) {
14582          *       setTimeout(function() {
14583          *         if (okToGreet(name)) {
14584          *           resolve('Hello, ' + name + '!');
14585          *         } else {
14586          *           reject('Greeting ' + name + ' is not allowed.');
14587          *         }
14588          *       }, 1000);
14589          *     });
14590          *   }
14591          *
14592          *   var promise = asyncGreet('Robin Hood');
14593          *   promise.then(function(greeting) {
14594          *     alert('Success: ' + greeting);
14595          *   }, function(reason) {
14596          *     alert('Failed: ' + reason);
14597          *   });
14598          * ```
14599          *
14600          * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14601          *
14602          * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14603          *
14604          * However, the more traditional CommonJS-style usage is still available, and documented below.
14605          *
14606          * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14607          * interface for interacting with an object that represents the result of an action that is
14608          * performed asynchronously, and may or may not be finished at any given point in time.
14609          *
14610          * From the perspective of dealing with error handling, deferred and promise APIs are to
14611          * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14612          *
14613          * ```js
14614          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14615          *   // are available in the current lexical scope (they could have been injected or passed in).
14616          *
14617          *   function asyncGreet(name) {
14618          *     var deferred = $q.defer();
14619          *
14620          *     setTimeout(function() {
14621          *       deferred.notify('About to greet ' + name + '.');
14622          *
14623          *       if (okToGreet(name)) {
14624          *         deferred.resolve('Hello, ' + name + '!');
14625          *       } else {
14626          *         deferred.reject('Greeting ' + name + ' is not allowed.');
14627          *       }
14628          *     }, 1000);
14629          *
14630          *     return deferred.promise;
14631          *   }
14632          *
14633          *   var promise = asyncGreet('Robin Hood');
14634          *   promise.then(function(greeting) {
14635          *     alert('Success: ' + greeting);
14636          *   }, function(reason) {
14637          *     alert('Failed: ' + reason);
14638          *   }, function(update) {
14639          *     alert('Got notification: ' + update);
14640          *   });
14641          * ```
14642          *
14643          * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14644          * comes in the way of guarantees that promise and deferred APIs make, see
14645          * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14646          *
14647          * Additionally the promise api allows for composition that is very hard to do with the
14648          * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14649          * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14650          * section on serial or parallel joining of promises.
14651          *
14652          * # The Deferred API
14653          *
14654          * A new instance of deferred is constructed by calling `$q.defer()`.
14655          *
14656          * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14657          * that can be used for signaling the successful or unsuccessful completion, as well as the status
14658          * of the task.
14659          *
14660          * **Methods**
14661          *
14662          * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14663          *   constructed via `$q.reject`, the promise will be rejected instead.
14664          * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14665          *   resolving it with a rejection constructed via `$q.reject`.
14666          * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14667          *   multiple times before the promise is either resolved or rejected.
14668          *
14669          * **Properties**
14670          *
14671          * - promise – `{Promise}` – promise object associated with this deferred.
14672          *
14673          *
14674          * # The Promise API
14675          *
14676          * A new promise instance is created when a deferred instance is created and can be retrieved by
14677          * calling `deferred.promise`.
14678          *
14679          * The purpose of the promise object is to allow for interested parties to get access to the result
14680          * of the deferred task when it completes.
14681          *
14682          * **Methods**
14683          *
14684          * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14685          *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14686          *   as soon as the result is available. The callbacks are called with a single argument: the result
14687          *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14688          *   provide a progress indication, before the promise is resolved or rejected.
14689          *
14690          *   This method *returns a new promise* which is resolved or rejected via the return value of the
14691          *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14692          *   with the value which is resolved in that promise using
14693          *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14694          *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14695          *   resolved or rejected from the notifyCallback method.
14696          *
14697          * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14698          *
14699          * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14700          *   but to do so without modifying the final value. This is useful to release resources or do some
14701          *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14702          *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14703          *   more information.
14704          *
14705          * # Chaining promises
14706          *
14707          * Because calling the `then` method of a promise returns a new derived promise, it is easily
14708          * possible to create a chain of promises:
14709          *
14710          * ```js
14711          *   promiseB = promiseA.then(function(result) {
14712          *     return result + 1;
14713          *   });
14714          *
14715          *   // promiseB will be resolved immediately after promiseA is resolved and its value
14716          *   // will be the result of promiseA incremented by 1
14717          * ```
14718          *
14719          * It is possible to create chains of any length and since a promise can be resolved with another
14720          * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14721          * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14722          * $http's response interceptors.
14723          *
14724          *
14725          * # Differences between Kris Kowal's Q and $q
14726          *
14727          *  There are two main differences:
14728          *
14729          * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14730          *   mechanism in angular, which means faster propagation of resolution or rejection into your
14731          *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14732          * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14733          *   all the important functionality needed for common async tasks.
14734          *
14735          *  # Testing
14736          *
14737          *  ```js
14738          *    it('should simulate promise', inject(function($q, $rootScope) {
14739          *      var deferred = $q.defer();
14740          *      var promise = deferred.promise;
14741          *      var resolvedValue;
14742          *
14743          *      promise.then(function(value) { resolvedValue = value; });
14744          *      expect(resolvedValue).toBeUndefined();
14745          *
14746          *      // Simulate resolving of promise
14747          *      deferred.resolve(123);
14748          *      // Note that the 'then' function does not get called synchronously.
14749          *      // This is because we want the promise API to always be async, whether or not
14750          *      // it got called synchronously or asynchronously.
14751          *      expect(resolvedValue).toBeUndefined();
14752          *
14753          *      // Propagate promise resolution to 'then' functions using $apply().
14754          *      $rootScope.$apply();
14755          *      expect(resolvedValue).toEqual(123);
14756          *    }));
14757          *  ```
14758          *
14759          * @param {function(function, function)} resolver Function which is responsible for resolving or
14760          *   rejecting the newly created promise. The first parameter is a function which resolves the
14761          *   promise, the second parameter is a function which rejects the promise.
14762          *
14763          * @returns {Promise} The newly created promise.
14764          */
14765         function $QProvider() {
14766
14767           this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14768             return qFactory(function(callback) {
14769               $rootScope.$evalAsync(callback);
14770             }, $exceptionHandler);
14771           }];
14772         }
14773
14774         function $$QProvider() {
14775           this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14776             return qFactory(function(callback) {
14777               $browser.defer(callback);
14778             }, $exceptionHandler);
14779           }];
14780         }
14781
14782         /**
14783          * Constructs a promise manager.
14784          *
14785          * @param {function(function)} nextTick Function for executing functions in the next turn.
14786          * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14787          *     debugging purposes.
14788          * @returns {object} Promise manager.
14789          */
14790         function qFactory(nextTick, exceptionHandler) {
14791           var $qMinErr = minErr('$q', TypeError);
14792           function callOnce(self, resolveFn, rejectFn) {
14793             var called = false;
14794             function wrap(fn) {
14795               return function(value) {
14796                 if (called) return;
14797                 called = true;
14798                 fn.call(self, value);
14799               };
14800             }
14801
14802             return [wrap(resolveFn), wrap(rejectFn)];
14803           }
14804
14805           /**
14806            * @ngdoc method
14807            * @name ng.$q#defer
14808            * @kind function
14809            *
14810            * @description
14811            * Creates a `Deferred` object which represents a task which will finish in the future.
14812            *
14813            * @returns {Deferred} Returns a new instance of deferred.
14814            */
14815           var defer = function() {
14816             return new Deferred();
14817           };
14818
14819           function Promise() {
14820             this.$$state = { status: 0 };
14821           }
14822
14823           extend(Promise.prototype, {
14824             then: function(onFulfilled, onRejected, progressBack) {
14825               if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14826                 return this;
14827               }
14828               var result = new Deferred();
14829
14830               this.$$state.pending = this.$$state.pending || [];
14831               this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14832               if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14833
14834               return result.promise;
14835             },
14836
14837             "catch": function(callback) {
14838               return this.then(null, callback);
14839             },
14840
14841             "finally": function(callback, progressBack) {
14842               return this.then(function(value) {
14843                 return handleCallback(value, true, callback);
14844               }, function(error) {
14845                 return handleCallback(error, false, callback);
14846               }, progressBack);
14847             }
14848           });
14849
14850           //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14851           function simpleBind(context, fn) {
14852             return function(value) {
14853               fn.call(context, value);
14854             };
14855           }
14856
14857           function processQueue(state) {
14858             var fn, deferred, pending;
14859
14860             pending = state.pending;
14861             state.processScheduled = false;
14862             state.pending = undefined;
14863             for (var i = 0, ii = pending.length; i < ii; ++i) {
14864               deferred = pending[i][0];
14865               fn = pending[i][state.status];
14866               try {
14867                 if (isFunction(fn)) {
14868                   deferred.resolve(fn(state.value));
14869                 } else if (state.status === 1) {
14870                   deferred.resolve(state.value);
14871                 } else {
14872                   deferred.reject(state.value);
14873                 }
14874               } catch (e) {
14875                 deferred.reject(e);
14876                 exceptionHandler(e);
14877               }
14878             }
14879           }
14880
14881           function scheduleProcessQueue(state) {
14882             if (state.processScheduled || !state.pending) return;
14883             state.processScheduled = true;
14884             nextTick(function() { processQueue(state); });
14885           }
14886
14887           function Deferred() {
14888             this.promise = new Promise();
14889             //Necessary to support unbound execution :/
14890             this.resolve = simpleBind(this, this.resolve);
14891             this.reject = simpleBind(this, this.reject);
14892             this.notify = simpleBind(this, this.notify);
14893           }
14894
14895           extend(Deferred.prototype, {
14896             resolve: function(val) {
14897               if (this.promise.$$state.status) return;
14898               if (val === this.promise) {
14899                 this.$$reject($qMinErr(
14900                   'qcycle',
14901                   "Expected promise to be resolved with value other than itself '{0}'",
14902                   val));
14903               } else {
14904                 this.$$resolve(val);
14905               }
14906
14907             },
14908
14909             $$resolve: function(val) {
14910               var then, fns;
14911
14912               fns = callOnce(this, this.$$resolve, this.$$reject);
14913               try {
14914                 if ((isObject(val) || isFunction(val))) then = val && val.then;
14915                 if (isFunction(then)) {
14916                   this.promise.$$state.status = -1;
14917                   then.call(val, fns[0], fns[1], this.notify);
14918                 } else {
14919                   this.promise.$$state.value = val;
14920                   this.promise.$$state.status = 1;
14921                   scheduleProcessQueue(this.promise.$$state);
14922                 }
14923               } catch (e) {
14924                 fns[1](e);
14925                 exceptionHandler(e);
14926               }
14927             },
14928
14929             reject: function(reason) {
14930               if (this.promise.$$state.status) return;
14931               this.$$reject(reason);
14932             },
14933
14934             $$reject: function(reason) {
14935               this.promise.$$state.value = reason;
14936               this.promise.$$state.status = 2;
14937               scheduleProcessQueue(this.promise.$$state);
14938             },
14939
14940             notify: function(progress) {
14941               var callbacks = this.promise.$$state.pending;
14942
14943               if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14944                 nextTick(function() {
14945                   var callback, result;
14946                   for (var i = 0, ii = callbacks.length; i < ii; i++) {
14947                     result = callbacks[i][0];
14948                     callback = callbacks[i][3];
14949                     try {
14950                       result.notify(isFunction(callback) ? callback(progress) : progress);
14951                     } catch (e) {
14952                       exceptionHandler(e);
14953                     }
14954                   }
14955                 });
14956               }
14957             }
14958           });
14959
14960           /**
14961            * @ngdoc method
14962            * @name $q#reject
14963            * @kind function
14964            *
14965            * @description
14966            * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14967            * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14968            * a promise chain, you don't need to worry about it.
14969            *
14970            * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14971            * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14972            * a promise error callback and you want to forward the error to the promise derived from the
14973            * current promise, you have to "rethrow" the error by returning a rejection constructed via
14974            * `reject`.
14975            *
14976            * ```js
14977            *   promiseB = promiseA.then(function(result) {
14978            *     // success: do something and resolve promiseB
14979            *     //          with the old or a new result
14980            *     return result;
14981            *   }, function(reason) {
14982            *     // error: handle the error if possible and
14983            *     //        resolve promiseB with newPromiseOrValue,
14984            *     //        otherwise forward the rejection to promiseB
14985            *     if (canHandle(reason)) {
14986            *      // handle the error and recover
14987            *      return newPromiseOrValue;
14988            *     }
14989            *     return $q.reject(reason);
14990            *   });
14991            * ```
14992            *
14993            * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14994            * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14995            */
14996           var reject = function(reason) {
14997             var result = new Deferred();
14998             result.reject(reason);
14999             return result.promise;
15000           };
15001
15002           var makePromise = function makePromise(value, resolved) {
15003             var result = new Deferred();
15004             if (resolved) {
15005               result.resolve(value);
15006             } else {
15007               result.reject(value);
15008             }
15009             return result.promise;
15010           };
15011
15012           var handleCallback = function handleCallback(value, isResolved, callback) {
15013             var callbackOutput = null;
15014             try {
15015               if (isFunction(callback)) callbackOutput = callback();
15016             } catch (e) {
15017               return makePromise(e, false);
15018             }
15019             if (isPromiseLike(callbackOutput)) {
15020               return callbackOutput.then(function() {
15021                 return makePromise(value, isResolved);
15022               }, function(error) {
15023                 return makePromise(error, false);
15024               });
15025             } else {
15026               return makePromise(value, isResolved);
15027             }
15028           };
15029
15030           /**
15031            * @ngdoc method
15032            * @name $q#when
15033            * @kind function
15034            *
15035            * @description
15036            * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15037            * This is useful when you are dealing with an object that might or might not be a promise, or if
15038            * the promise comes from a source that can't be trusted.
15039            *
15040            * @param {*} value Value or a promise
15041            * @param {Function=} successCallback
15042            * @param {Function=} errorCallback
15043            * @param {Function=} progressCallback
15044            * @returns {Promise} Returns a promise of the passed value or promise
15045            */
15046
15047
15048           var when = function(value, callback, errback, progressBack) {
15049             var result = new Deferred();
15050             result.resolve(value);
15051             return result.promise.then(callback, errback, progressBack);
15052           };
15053
15054           /**
15055            * @ngdoc method
15056            * @name $q#resolve
15057            * @kind function
15058            *
15059            * @description
15060            * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15061            *
15062            * @param {*} value Value or a promise
15063            * @param {Function=} successCallback
15064            * @param {Function=} errorCallback
15065            * @param {Function=} progressCallback
15066            * @returns {Promise} Returns a promise of the passed value or promise
15067            */
15068           var resolve = when;
15069
15070           /**
15071            * @ngdoc method
15072            * @name $q#all
15073            * @kind function
15074            *
15075            * @description
15076            * Combines multiple promises into a single promise that is resolved when all of the input
15077            * promises are resolved.
15078            *
15079            * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15080            * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15081            *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15082            *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15083            *   with the same rejection value.
15084            */
15085
15086           function all(promises) {
15087             var deferred = new Deferred(),
15088                 counter = 0,
15089                 results = isArray(promises) ? [] : {};
15090
15091             forEach(promises, function(promise, key) {
15092               counter++;
15093               when(promise).then(function(value) {
15094                 if (results.hasOwnProperty(key)) return;
15095                 results[key] = value;
15096                 if (!(--counter)) deferred.resolve(results);
15097               }, function(reason) {
15098                 if (results.hasOwnProperty(key)) return;
15099                 deferred.reject(reason);
15100               });
15101             });
15102
15103             if (counter === 0) {
15104               deferred.resolve(results);
15105             }
15106
15107             return deferred.promise;
15108           }
15109
15110           var $Q = function Q(resolver) {
15111             if (!isFunction(resolver)) {
15112               throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15113             }
15114
15115             if (!(this instanceof Q)) {
15116               // More useful when $Q is the Promise itself.
15117               return new Q(resolver);
15118             }
15119
15120             var deferred = new Deferred();
15121
15122             function resolveFn(value) {
15123               deferred.resolve(value);
15124             }
15125
15126             function rejectFn(reason) {
15127               deferred.reject(reason);
15128             }
15129
15130             resolver(resolveFn, rejectFn);
15131
15132             return deferred.promise;
15133           };
15134
15135           $Q.defer = defer;
15136           $Q.reject = reject;
15137           $Q.when = when;
15138           $Q.resolve = resolve;
15139           $Q.all = all;
15140
15141           return $Q;
15142         }
15143
15144         function $$RAFProvider() { //rAF
15145           this.$get = ['$window', '$timeout', function($window, $timeout) {
15146             var requestAnimationFrame = $window.requestAnimationFrame ||
15147                                         $window.webkitRequestAnimationFrame;
15148
15149             var cancelAnimationFrame = $window.cancelAnimationFrame ||
15150                                        $window.webkitCancelAnimationFrame ||
15151                                        $window.webkitCancelRequestAnimationFrame;
15152
15153             var rafSupported = !!requestAnimationFrame;
15154             var raf = rafSupported
15155               ? function(fn) {
15156                   var id = requestAnimationFrame(fn);
15157                   return function() {
15158                     cancelAnimationFrame(id);
15159                   };
15160                 }
15161               : function(fn) {
15162                   var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15163                   return function() {
15164                     $timeout.cancel(timer);
15165                   };
15166                 };
15167
15168             raf.supported = rafSupported;
15169
15170             return raf;
15171           }];
15172         }
15173
15174         /**
15175          * DESIGN NOTES
15176          *
15177          * The design decisions behind the scope are heavily favored for speed and memory consumption.
15178          *
15179          * The typical use of scope is to watch the expressions, which most of the time return the same
15180          * value as last time so we optimize the operation.
15181          *
15182          * Closures construction is expensive in terms of speed as well as memory:
15183          *   - No closures, instead use prototypical inheritance for API
15184          *   - Internal state needs to be stored on scope directly, which means that private state is
15185          *     exposed as $$____ properties
15186          *
15187          * Loop operations are optimized by using while(count--) { ... }
15188          *   - This means that in order to keep the same order of execution as addition we have to add
15189          *     items to the array at the beginning (unshift) instead of at the end (push)
15190          *
15191          * Child scopes are created and removed often
15192          *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15193          *
15194          * There are fewer watches than observers. This is why you don't want the observer to be implemented
15195          * in the same way as watch. Watch requires return of the initialization function which is expensive
15196          * to construct.
15197          */
15198
15199
15200         /**
15201          * @ngdoc provider
15202          * @name $rootScopeProvider
15203          * @description
15204          *
15205          * Provider for the $rootScope service.
15206          */
15207
15208         /**
15209          * @ngdoc method
15210          * @name $rootScopeProvider#digestTtl
15211          * @description
15212          *
15213          * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15214          * assuming that the model is unstable.
15215          *
15216          * The current default is 10 iterations.
15217          *
15218          * In complex applications it's possible that the dependencies between `$watch`s will result in
15219          * several digest iterations. However if an application needs more than the default 10 digest
15220          * iterations for its model to stabilize then you should investigate what is causing the model to
15221          * continuously change during the digest.
15222          *
15223          * Increasing the TTL could have performance implications, so you should not change it without
15224          * proper justification.
15225          *
15226          * @param {number} limit The number of digest iterations.
15227          */
15228
15229
15230         /**
15231          * @ngdoc service
15232          * @name $rootScope
15233          * @description
15234          *
15235          * Every application has a single root {@link ng.$rootScope.Scope scope}.
15236          * All other scopes are descendant scopes of the root scope. Scopes provide separation
15237          * between the model and the view, via a mechanism for watching the model for changes.
15238          * They also provide event emission/broadcast and subscription facility. See the
15239          * {@link guide/scope developer guide on scopes}.
15240          */
15241         function $RootScopeProvider() {
15242           var TTL = 10;
15243           var $rootScopeMinErr = minErr('$rootScope');
15244           var lastDirtyWatch = null;
15245           var applyAsyncId = null;
15246
15247           this.digestTtl = function(value) {
15248             if (arguments.length) {
15249               TTL = value;
15250             }
15251             return TTL;
15252           };
15253
15254           function createChildScopeClass(parent) {
15255             function ChildScope() {
15256               this.$$watchers = this.$$nextSibling =
15257                   this.$$childHead = this.$$childTail = null;
15258               this.$$listeners = {};
15259               this.$$listenerCount = {};
15260               this.$$watchersCount = 0;
15261               this.$id = nextUid();
15262               this.$$ChildScope = null;
15263             }
15264             ChildScope.prototype = parent;
15265             return ChildScope;
15266           }
15267
15268           this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15269               function($injector, $exceptionHandler, $parse, $browser) {
15270
15271             function destroyChildScope($event) {
15272                 $event.currentScope.$$destroyed = true;
15273             }
15274
15275             function cleanUpScope($scope) {
15276
15277               if (msie === 9) {
15278                 // There is a memory leak in IE9 if all child scopes are not disconnected
15279                 // completely when a scope is destroyed. So this code will recurse up through
15280                 // all this scopes children
15281                 //
15282                 // See issue https://github.com/angular/angular.js/issues/10706
15283                 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15284                 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15285               }
15286
15287               // The code below works around IE9 and V8's memory leaks
15288               //
15289               // See:
15290               // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15291               // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15292               // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15293
15294               $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15295                   $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15296             }
15297
15298             /**
15299              * @ngdoc type
15300              * @name $rootScope.Scope
15301              *
15302              * @description
15303              * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15304              * {@link auto.$injector $injector}. Child scopes are created using the
15305              * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15306              * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15307              * an in-depth introduction and usage examples.
15308              *
15309              *
15310              * # Inheritance
15311              * A scope can inherit from a parent scope, as in this example:
15312              * ```js
15313                  var parent = $rootScope;
15314                  var child = parent.$new();
15315
15316                  parent.salutation = "Hello";
15317                  expect(child.salutation).toEqual('Hello');
15318
15319                  child.salutation = "Welcome";
15320                  expect(child.salutation).toEqual('Welcome');
15321                  expect(parent.salutation).toEqual('Hello');
15322              * ```
15323              *
15324              * When interacting with `Scope` in tests, additional helper methods are available on the
15325              * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15326              * details.
15327              *
15328              *
15329              * @param {Object.<string, function()>=} providers Map of service factory which need to be
15330              *                                       provided for the current scope. Defaults to {@link ng}.
15331              * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15332              *                              append/override services provided by `providers`. This is handy
15333              *                              when unit-testing and having the need to override a default
15334              *                              service.
15335              * @returns {Object} Newly created scope.
15336              *
15337              */
15338             function Scope() {
15339               this.$id = nextUid();
15340               this.$$phase = this.$parent = this.$$watchers =
15341                              this.$$nextSibling = this.$$prevSibling =
15342                              this.$$childHead = this.$$childTail = null;
15343               this.$root = this;
15344               this.$$destroyed = false;
15345               this.$$listeners = {};
15346               this.$$listenerCount = {};
15347               this.$$watchersCount = 0;
15348               this.$$isolateBindings = null;
15349             }
15350
15351             /**
15352              * @ngdoc property
15353              * @name $rootScope.Scope#$id
15354              *
15355              * @description
15356              * Unique scope ID (monotonically increasing) useful for debugging.
15357              */
15358
15359              /**
15360               * @ngdoc property
15361               * @name $rootScope.Scope#$parent
15362               *
15363               * @description
15364               * Reference to the parent scope.
15365               */
15366
15367               /**
15368                * @ngdoc property
15369                * @name $rootScope.Scope#$root
15370                *
15371                * @description
15372                * Reference to the root scope.
15373                */
15374
15375             Scope.prototype = {
15376               constructor: Scope,
15377               /**
15378                * @ngdoc method
15379                * @name $rootScope.Scope#$new
15380                * @kind function
15381                *
15382                * @description
15383                * Creates a new child {@link ng.$rootScope.Scope scope}.
15384                *
15385                * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15386                * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15387                *
15388                * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15389                * desired for the scope and its child scopes to be permanently detached from the parent and
15390                * thus stop participating in model change detection and listener notification by invoking.
15391                *
15392                * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15393                *         parent scope. The scope is isolated, as it can not see parent scope properties.
15394                *         When creating widgets, it is useful for the widget to not accidentally read parent
15395                *         state.
15396                *
15397                * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15398                *                              of the newly created scope. Defaults to `this` scope if not provided.
15399                *                              This is used when creating a transclude scope to correctly place it
15400                *                              in the scope hierarchy while maintaining the correct prototypical
15401                *                              inheritance.
15402                *
15403                * @returns {Object} The newly created child scope.
15404                *
15405                */
15406               $new: function(isolate, parent) {
15407                 var child;
15408
15409                 parent = parent || this;
15410
15411                 if (isolate) {
15412                   child = new Scope();
15413                   child.$root = this.$root;
15414                 } else {
15415                   // Only create a child scope class if somebody asks for one,
15416                   // but cache it to allow the VM to optimize lookups.
15417                   if (!this.$$ChildScope) {
15418                     this.$$ChildScope = createChildScopeClass(this);
15419                   }
15420                   child = new this.$$ChildScope();
15421                 }
15422                 child.$parent = parent;
15423                 child.$$prevSibling = parent.$$childTail;
15424                 if (parent.$$childHead) {
15425                   parent.$$childTail.$$nextSibling = child;
15426                   parent.$$childTail = child;
15427                 } else {
15428                   parent.$$childHead = parent.$$childTail = child;
15429                 }
15430
15431                 // When the new scope is not isolated or we inherit from `this`, and
15432                 // the parent scope is destroyed, the property `$$destroyed` is inherited
15433                 // prototypically. In all other cases, this property needs to be set
15434                 // when the parent scope is destroyed.
15435                 // The listener needs to be added after the parent is set
15436                 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15437
15438                 return child;
15439               },
15440
15441               /**
15442                * @ngdoc method
15443                * @name $rootScope.Scope#$watch
15444                * @kind function
15445                *
15446                * @description
15447                * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15448                *
15449                * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15450                *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15451                *   its value when executed multiple times with the same input because it may be executed multiple
15452                *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15453                *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15454                * - The `listener` is called only when the value from the current `watchExpression` and the
15455                *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15456                *   see below). Inequality is determined according to reference inequality,
15457                *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15458                *    via the `!==` Javascript operator, unless `objectEquality == true`
15459                *   (see next point)
15460                * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15461                *   according to the {@link angular.equals} function. To save the value of the object for
15462                *   later comparison, the {@link angular.copy} function is used. This therefore means that
15463                *   watching complex objects will have adverse memory and performance implications.
15464                * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15465                *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15466                *   iteration limit is 10 to prevent an infinite loop deadlock.
15467                *
15468                *
15469                * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15470                * you can register a `watchExpression` function with no `listener`. (Be prepared for
15471                * multiple calls to your `watchExpression` because it will execute multiple times in a
15472                * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15473                *
15474                * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15475                * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15476                * watcher. In rare cases, this is undesirable because the listener is called when the result
15477                * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15478                * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15479                * listener was called due to initialization.
15480                *
15481                *
15482                *
15483                * # Example
15484                * ```js
15485                    // let's assume that scope was dependency injected as the $rootScope
15486                    var scope = $rootScope;
15487                    scope.name = 'misko';
15488                    scope.counter = 0;
15489
15490                    expect(scope.counter).toEqual(0);
15491                    scope.$watch('name', function(newValue, oldValue) {
15492                      scope.counter = scope.counter + 1;
15493                    });
15494                    expect(scope.counter).toEqual(0);
15495
15496                    scope.$digest();
15497                    // the listener is always called during the first $digest loop after it was registered
15498                    expect(scope.counter).toEqual(1);
15499
15500                    scope.$digest();
15501                    // but now it will not be called unless the value changes
15502                    expect(scope.counter).toEqual(1);
15503
15504                    scope.name = 'adam';
15505                    scope.$digest();
15506                    expect(scope.counter).toEqual(2);
15507
15508
15509
15510                    // Using a function as a watchExpression
15511                    var food;
15512                    scope.foodCounter = 0;
15513                    expect(scope.foodCounter).toEqual(0);
15514                    scope.$watch(
15515                      // This function returns the value being watched. It is called for each turn of the $digest loop
15516                      function() { return food; },
15517                      // This is the change listener, called when the value returned from the above function changes
15518                      function(newValue, oldValue) {
15519                        if ( newValue !== oldValue ) {
15520                          // Only increment the counter if the value changed
15521                          scope.foodCounter = scope.foodCounter + 1;
15522                        }
15523                      }
15524                    );
15525                    // No digest has been run so the counter will be zero
15526                    expect(scope.foodCounter).toEqual(0);
15527
15528                    // Run the digest but since food has not changed count will still be zero
15529                    scope.$digest();
15530                    expect(scope.foodCounter).toEqual(0);
15531
15532                    // Update food and run digest.  Now the counter will increment
15533                    food = 'cheeseburger';
15534                    scope.$digest();
15535                    expect(scope.foodCounter).toEqual(1);
15536
15537                * ```
15538                *
15539                *
15540                *
15541                * @param {(function()|string)} watchExpression Expression that is evaluated on each
15542                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15543                *    a call to the `listener`.
15544                *
15545                *    - `string`: Evaluated as {@link guide/expression expression}
15546                *    - `function(scope)`: called with current `scope` as a parameter.
15547                * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15548                *    of `watchExpression` changes.
15549                *
15550                *    - `newVal` contains the current value of the `watchExpression`
15551                *    - `oldVal` contains the previous value of the `watchExpression`
15552                *    - `scope` refers to the current scope
15553                * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15554                *     comparing for reference equality.
15555                * @returns {function()} Returns a deregistration function for this listener.
15556                */
15557               $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15558                 var get = $parse(watchExp);
15559
15560                 if (get.$$watchDelegate) {
15561                   return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15562                 }
15563                 var scope = this,
15564                     array = scope.$$watchers,
15565                     watcher = {
15566                       fn: listener,
15567                       last: initWatchVal,
15568                       get: get,
15569                       exp: prettyPrintExpression || watchExp,
15570                       eq: !!objectEquality
15571                     };
15572
15573                 lastDirtyWatch = null;
15574
15575                 if (!isFunction(listener)) {
15576                   watcher.fn = noop;
15577                 }
15578
15579                 if (!array) {
15580                   array = scope.$$watchers = [];
15581                 }
15582                 // we use unshift since we use a while loop in $digest for speed.
15583                 // the while loop reads in reverse order.
15584                 array.unshift(watcher);
15585                 incrementWatchersCount(this, 1);
15586
15587                 return function deregisterWatch() {
15588                   if (arrayRemove(array, watcher) >= 0) {
15589                     incrementWatchersCount(scope, -1);
15590                   }
15591                   lastDirtyWatch = null;
15592                 };
15593               },
15594
15595               /**
15596                * @ngdoc method
15597                * @name $rootScope.Scope#$watchGroup
15598                * @kind function
15599                *
15600                * @description
15601                * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15602                * If any one expression in the collection changes the `listener` is executed.
15603                *
15604                * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15605                *   call to $digest() to see if any items changes.
15606                * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15607                *
15608                * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15609                * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15610                *
15611                * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15612                *    expression in `watchExpressions` changes
15613                *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15614                *    those of `watchExpression`
15615                *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15616                *    those of `watchExpression`
15617                *    The `scope` refers to the current scope.
15618                * @returns {function()} Returns a de-registration function for all listeners.
15619                */
15620               $watchGroup: function(watchExpressions, listener) {
15621                 var oldValues = new Array(watchExpressions.length);
15622                 var newValues = new Array(watchExpressions.length);
15623                 var deregisterFns = [];
15624                 var self = this;
15625                 var changeReactionScheduled = false;
15626                 var firstRun = true;
15627
15628                 if (!watchExpressions.length) {
15629                   // No expressions means we call the listener ASAP
15630                   var shouldCall = true;
15631                   self.$evalAsync(function() {
15632                     if (shouldCall) listener(newValues, newValues, self);
15633                   });
15634                   return function deregisterWatchGroup() {
15635                     shouldCall = false;
15636                   };
15637                 }
15638
15639                 if (watchExpressions.length === 1) {
15640                   // Special case size of one
15641                   return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15642                     newValues[0] = value;
15643                     oldValues[0] = oldValue;
15644                     listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15645                   });
15646                 }
15647
15648                 forEach(watchExpressions, function(expr, i) {
15649                   var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15650                     newValues[i] = value;
15651                     oldValues[i] = oldValue;
15652                     if (!changeReactionScheduled) {
15653                       changeReactionScheduled = true;
15654                       self.$evalAsync(watchGroupAction);
15655                     }
15656                   });
15657                   deregisterFns.push(unwatchFn);
15658                 });
15659
15660                 function watchGroupAction() {
15661                   changeReactionScheduled = false;
15662
15663                   if (firstRun) {
15664                     firstRun = false;
15665                     listener(newValues, newValues, self);
15666                   } else {
15667                     listener(newValues, oldValues, self);
15668                   }
15669                 }
15670
15671                 return function deregisterWatchGroup() {
15672                   while (deregisterFns.length) {
15673                     deregisterFns.shift()();
15674                   }
15675                 };
15676               },
15677
15678
15679               /**
15680                * @ngdoc method
15681                * @name $rootScope.Scope#$watchCollection
15682                * @kind function
15683                *
15684                * @description
15685                * Shallow watches the properties of an object and fires whenever any of the properties change
15686                * (for arrays, this implies watching the array items; for object maps, this implies watching
15687                * the properties). If a change is detected, the `listener` callback is fired.
15688                *
15689                * - The `obj` collection is observed via standard $watch operation and is examined on every
15690                *   call to $digest() to see if any items have been added, removed, or moved.
15691                * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15692                *   adding, removing, and moving items belonging to an object or array.
15693                *
15694                *
15695                * # Example
15696                * ```js
15697                   $scope.names = ['igor', 'matias', 'misko', 'james'];
15698                   $scope.dataCount = 4;
15699
15700                   $scope.$watchCollection('names', function(newNames, oldNames) {
15701                     $scope.dataCount = newNames.length;
15702                   });
15703
15704                   expect($scope.dataCount).toEqual(4);
15705                   $scope.$digest();
15706
15707                   //still at 4 ... no changes
15708                   expect($scope.dataCount).toEqual(4);
15709
15710                   $scope.names.pop();
15711                   $scope.$digest();
15712
15713                   //now there's been a change
15714                   expect($scope.dataCount).toEqual(3);
15715                * ```
15716                *
15717                *
15718                * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15719                *    expression value should evaluate to an object or an array which is observed on each
15720                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15721                *    collection will trigger a call to the `listener`.
15722                *
15723                * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15724                *    when a change is detected.
15725                *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15726                *    - The `oldCollection` object is a copy of the former collection data.
15727                *      Due to performance considerations, the`oldCollection` value is computed only if the
15728                *      `listener` function declares two or more arguments.
15729                *    - The `scope` argument refers to the current scope.
15730                *
15731                * @returns {function()} Returns a de-registration function for this listener. When the
15732                *    de-registration function is executed, the internal watch operation is terminated.
15733                */
15734               $watchCollection: function(obj, listener) {
15735                 $watchCollectionInterceptor.$stateful = true;
15736
15737                 var self = this;
15738                 // the current value, updated on each dirty-check run
15739                 var newValue;
15740                 // a shallow copy of the newValue from the last dirty-check run,
15741                 // updated to match newValue during dirty-check run
15742                 var oldValue;
15743                 // a shallow copy of the newValue from when the last change happened
15744                 var veryOldValue;
15745                 // only track veryOldValue if the listener is asking for it
15746                 var trackVeryOldValue = (listener.length > 1);
15747                 var changeDetected = 0;
15748                 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15749                 var internalArray = [];
15750                 var internalObject = {};
15751                 var initRun = true;
15752                 var oldLength = 0;
15753
15754                 function $watchCollectionInterceptor(_value) {
15755                   newValue = _value;
15756                   var newLength, key, bothNaN, newItem, oldItem;
15757
15758                   // If the new value is undefined, then return undefined as the watch may be a one-time watch
15759                   if (isUndefined(newValue)) return;
15760
15761                   if (!isObject(newValue)) { // if primitive
15762                     if (oldValue !== newValue) {
15763                       oldValue = newValue;
15764                       changeDetected++;
15765                     }
15766                   } else if (isArrayLike(newValue)) {
15767                     if (oldValue !== internalArray) {
15768                       // we are transitioning from something which was not an array into array.
15769                       oldValue = internalArray;
15770                       oldLength = oldValue.length = 0;
15771                       changeDetected++;
15772                     }
15773
15774                     newLength = newValue.length;
15775
15776                     if (oldLength !== newLength) {
15777                       // if lengths do not match we need to trigger change notification
15778                       changeDetected++;
15779                       oldValue.length = oldLength = newLength;
15780                     }
15781                     // copy the items to oldValue and look for changes.
15782                     for (var i = 0; i < newLength; i++) {
15783                       oldItem = oldValue[i];
15784                       newItem = newValue[i];
15785
15786                       bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15787                       if (!bothNaN && (oldItem !== newItem)) {
15788                         changeDetected++;
15789                         oldValue[i] = newItem;
15790                       }
15791                     }
15792                   } else {
15793                     if (oldValue !== internalObject) {
15794                       // we are transitioning from something which was not an object into object.
15795                       oldValue = internalObject = {};
15796                       oldLength = 0;
15797                       changeDetected++;
15798                     }
15799                     // copy the items to oldValue and look for changes.
15800                     newLength = 0;
15801                     for (key in newValue) {
15802                       if (hasOwnProperty.call(newValue, key)) {
15803                         newLength++;
15804                         newItem = newValue[key];
15805                         oldItem = oldValue[key];
15806
15807                         if (key in oldValue) {
15808                           bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15809                           if (!bothNaN && (oldItem !== newItem)) {
15810                             changeDetected++;
15811                             oldValue[key] = newItem;
15812                           }
15813                         } else {
15814                           oldLength++;
15815                           oldValue[key] = newItem;
15816                           changeDetected++;
15817                         }
15818                       }
15819                     }
15820                     if (oldLength > newLength) {
15821                       // we used to have more keys, need to find them and destroy them.
15822                       changeDetected++;
15823                       for (key in oldValue) {
15824                         if (!hasOwnProperty.call(newValue, key)) {
15825                           oldLength--;
15826                           delete oldValue[key];
15827                         }
15828                       }
15829                     }
15830                   }
15831                   return changeDetected;
15832                 }
15833
15834                 function $watchCollectionAction() {
15835                   if (initRun) {
15836                     initRun = false;
15837                     listener(newValue, newValue, self);
15838                   } else {
15839                     listener(newValue, veryOldValue, self);
15840                   }
15841
15842                   // make a copy for the next time a collection is changed
15843                   if (trackVeryOldValue) {
15844                     if (!isObject(newValue)) {
15845                       //primitive
15846                       veryOldValue = newValue;
15847                     } else if (isArrayLike(newValue)) {
15848                       veryOldValue = new Array(newValue.length);
15849                       for (var i = 0; i < newValue.length; i++) {
15850                         veryOldValue[i] = newValue[i];
15851                       }
15852                     } else { // if object
15853                       veryOldValue = {};
15854                       for (var key in newValue) {
15855                         if (hasOwnProperty.call(newValue, key)) {
15856                           veryOldValue[key] = newValue[key];
15857                         }
15858                       }
15859                     }
15860                   }
15861                 }
15862
15863                 return this.$watch(changeDetector, $watchCollectionAction);
15864               },
15865
15866               /**
15867                * @ngdoc method
15868                * @name $rootScope.Scope#$digest
15869                * @kind function
15870                *
15871                * @description
15872                * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15873                * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15874                * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15875                * until no more listeners are firing. This means that it is possible to get into an infinite
15876                * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15877                * iterations exceeds 10.
15878                *
15879                * Usually, you don't call `$digest()` directly in
15880                * {@link ng.directive:ngController controllers} or in
15881                * {@link ng.$compileProvider#directive directives}.
15882                * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15883                * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15884                *
15885                * If you want to be notified whenever `$digest()` is called,
15886                * you can register a `watchExpression` function with
15887                * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15888                *
15889                * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15890                *
15891                * # Example
15892                * ```js
15893                    var scope = ...;
15894                    scope.name = 'misko';
15895                    scope.counter = 0;
15896
15897                    expect(scope.counter).toEqual(0);
15898                    scope.$watch('name', function(newValue, oldValue) {
15899                      scope.counter = scope.counter + 1;
15900                    });
15901                    expect(scope.counter).toEqual(0);
15902
15903                    scope.$digest();
15904                    // the listener is always called during the first $digest loop after it was registered
15905                    expect(scope.counter).toEqual(1);
15906
15907                    scope.$digest();
15908                    // but now it will not be called unless the value changes
15909                    expect(scope.counter).toEqual(1);
15910
15911                    scope.name = 'adam';
15912                    scope.$digest();
15913                    expect(scope.counter).toEqual(2);
15914                * ```
15915                *
15916                */
15917               $digest: function() {
15918                 var watch, value, last,
15919                     watchers,
15920                     length,
15921                     dirty, ttl = TTL,
15922                     next, current, target = this,
15923                     watchLog = [],
15924                     logIdx, logMsg, asyncTask;
15925
15926                 beginPhase('$digest');
15927                 // Check for changes to browser url that happened in sync before the call to $digest
15928                 $browser.$$checkUrlChange();
15929
15930                 if (this === $rootScope && applyAsyncId !== null) {
15931                   // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15932                   // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15933                   $browser.defer.cancel(applyAsyncId);
15934                   flushApplyAsync();
15935                 }
15936
15937                 lastDirtyWatch = null;
15938
15939                 do { // "while dirty" loop
15940                   dirty = false;
15941                   current = target;
15942
15943                   while (asyncQueue.length) {
15944                     try {
15945                       asyncTask = asyncQueue.shift();
15946                       asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15947                     } catch (e) {
15948                       $exceptionHandler(e);
15949                     }
15950                     lastDirtyWatch = null;
15951                   }
15952
15953                   traverseScopesLoop:
15954                   do { // "traverse the scopes" loop
15955                     if ((watchers = current.$$watchers)) {
15956                       // process our watches
15957                       length = watchers.length;
15958                       while (length--) {
15959                         try {
15960                           watch = watchers[length];
15961                           // Most common watches are on primitives, in which case we can short
15962                           // circuit it with === operator, only when === fails do we use .equals
15963                           if (watch) {
15964                             if ((value = watch.get(current)) !== (last = watch.last) &&
15965                                 !(watch.eq
15966                                     ? equals(value, last)
15967                                     : (typeof value === 'number' && typeof last === 'number'
15968                                        && isNaN(value) && isNaN(last)))) {
15969                               dirty = true;
15970                               lastDirtyWatch = watch;
15971                               watch.last = watch.eq ? copy(value, null) : value;
15972                               watch.fn(value, ((last === initWatchVal) ? value : last), current);
15973                               if (ttl < 5) {
15974                                 logIdx = 4 - ttl;
15975                                 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15976                                 watchLog[logIdx].push({
15977                                   msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15978                                   newVal: value,
15979                                   oldVal: last
15980                                 });
15981                               }
15982                             } else if (watch === lastDirtyWatch) {
15983                               // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15984                               // have already been tested.
15985                               dirty = false;
15986                               break traverseScopesLoop;
15987                             }
15988                           }
15989                         } catch (e) {
15990                           $exceptionHandler(e);
15991                         }
15992                       }
15993                     }
15994
15995                     // Insanity Warning: scope depth-first traversal
15996                     // yes, this code is a bit crazy, but it works and we have tests to prove it!
15997                     // this piece should be kept in sync with the traversal in $broadcast
15998                     if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15999                         (current !== target && current.$$nextSibling)))) {
16000                       while (current !== target && !(next = current.$$nextSibling)) {
16001                         current = current.$parent;
16002                       }
16003                     }
16004                   } while ((current = next));
16005
16006                   // `break traverseScopesLoop;` takes us to here
16007
16008                   if ((dirty || asyncQueue.length) && !(ttl--)) {
16009                     clearPhase();
16010                     throw $rootScopeMinErr('infdig',
16011                         '{0} $digest() iterations reached. Aborting!\n' +
16012                         'Watchers fired in the last 5 iterations: {1}',
16013                         TTL, watchLog);
16014                   }
16015
16016                 } while (dirty || asyncQueue.length);
16017
16018                 clearPhase();
16019
16020                 while (postDigestQueue.length) {
16021                   try {
16022                     postDigestQueue.shift()();
16023                   } catch (e) {
16024                     $exceptionHandler(e);
16025                   }
16026                 }
16027               },
16028
16029
16030               /**
16031                * @ngdoc event
16032                * @name $rootScope.Scope#$destroy
16033                * @eventType broadcast on scope being destroyed
16034                *
16035                * @description
16036                * Broadcasted when a scope and its children are being destroyed.
16037                *
16038                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16039                * clean up DOM bindings before an element is removed from the DOM.
16040                */
16041
16042               /**
16043                * @ngdoc method
16044                * @name $rootScope.Scope#$destroy
16045                * @kind function
16046                *
16047                * @description
16048                * Removes the current scope (and all of its children) from the parent scope. Removal implies
16049                * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16050                * propagate to the current scope and its children. Removal also implies that the current
16051                * scope is eligible for garbage collection.
16052                *
16053                * The `$destroy()` is usually used by directives such as
16054                * {@link ng.directive:ngRepeat ngRepeat} for managing the
16055                * unrolling of the loop.
16056                *
16057                * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16058                * Application code can register a `$destroy` event handler that will give it a chance to
16059                * perform any necessary cleanup.
16060                *
16061                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16062                * clean up DOM bindings before an element is removed from the DOM.
16063                */
16064               $destroy: function() {
16065                 // We can't destroy a scope that has been already destroyed.
16066                 if (this.$$destroyed) return;
16067                 var parent = this.$parent;
16068
16069                 this.$broadcast('$destroy');
16070                 this.$$destroyed = true;
16071
16072                 if (this === $rootScope) {
16073                   //Remove handlers attached to window when $rootScope is removed
16074                   $browser.$$applicationDestroyed();
16075                 }
16076
16077                 incrementWatchersCount(this, -this.$$watchersCount);
16078                 for (var eventName in this.$$listenerCount) {
16079                   decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16080                 }
16081
16082                 // sever all the references to parent scopes (after this cleanup, the current scope should
16083                 // not be retained by any of our references and should be eligible for garbage collection)
16084                 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16085                 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16086                 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16087                 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16088
16089                 // Disable listeners, watchers and apply/digest methods
16090                 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16091                 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16092                 this.$$listeners = {};
16093
16094                 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16095                 this.$$nextSibling = null;
16096                 cleanUpScope(this);
16097               },
16098
16099               /**
16100                * @ngdoc method
16101                * @name $rootScope.Scope#$eval
16102                * @kind function
16103                *
16104                * @description
16105                * Executes the `expression` on the current scope and returns the result. Any exceptions in
16106                * the expression are propagated (uncaught). This is useful when evaluating Angular
16107                * expressions.
16108                *
16109                * # Example
16110                * ```js
16111                    var scope = ng.$rootScope.Scope();
16112                    scope.a = 1;
16113                    scope.b = 2;
16114
16115                    expect(scope.$eval('a+b')).toEqual(3);
16116                    expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16117                * ```
16118                *
16119                * @param {(string|function())=} expression An angular expression to be executed.
16120                *
16121                *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16122                *    - `function(scope)`: execute the function with the current `scope` parameter.
16123                *
16124                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16125                * @returns {*} The result of evaluating the expression.
16126                */
16127               $eval: function(expr, locals) {
16128                 return $parse(expr)(this, locals);
16129               },
16130
16131               /**
16132                * @ngdoc method
16133                * @name $rootScope.Scope#$evalAsync
16134                * @kind function
16135                *
16136                * @description
16137                * Executes the expression on the current scope at a later point in time.
16138                *
16139                * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16140                * that:
16141                *
16142                *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16143                *     rendering).
16144                *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16145                *     `expression` execution.
16146                *
16147                * Any exceptions from the execution of the expression are forwarded to the
16148                * {@link ng.$exceptionHandler $exceptionHandler} service.
16149                *
16150                * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16151                * will be scheduled. However, it is encouraged to always call code that changes the model
16152                * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16153                *
16154                * @param {(string|function())=} expression An angular expression to be executed.
16155                *
16156                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16157                *    - `function(scope)`: execute the function with the current `scope` parameter.
16158                *
16159                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16160                */
16161               $evalAsync: function(expr, locals) {
16162                 // if we are outside of an $digest loop and this is the first time we are scheduling async
16163                 // task also schedule async auto-flush
16164                 if (!$rootScope.$$phase && !asyncQueue.length) {
16165                   $browser.defer(function() {
16166                     if (asyncQueue.length) {
16167                       $rootScope.$digest();
16168                     }
16169                   });
16170                 }
16171
16172                 asyncQueue.push({scope: this, expression: expr, locals: locals});
16173               },
16174
16175               $$postDigest: function(fn) {
16176                 postDigestQueue.push(fn);
16177               },
16178
16179               /**
16180                * @ngdoc method
16181                * @name $rootScope.Scope#$apply
16182                * @kind function
16183                *
16184                * @description
16185                * `$apply()` is used to execute an expression in angular from outside of the angular
16186                * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16187                * Because we are calling into the angular framework we need to perform proper scope life
16188                * cycle of {@link ng.$exceptionHandler exception handling},
16189                * {@link ng.$rootScope.Scope#$digest executing watches}.
16190                *
16191                * ## Life cycle
16192                *
16193                * # Pseudo-Code of `$apply()`
16194                * ```js
16195                    function $apply(expr) {
16196                      try {
16197                        return $eval(expr);
16198                      } catch (e) {
16199                        $exceptionHandler(e);
16200                      } finally {
16201                        $root.$digest();
16202                      }
16203                    }
16204                * ```
16205                *
16206                *
16207                * Scope's `$apply()` method transitions through the following stages:
16208                *
16209                * 1. The {@link guide/expression expression} is executed using the
16210                *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16211                * 2. Any exceptions from the execution of the expression are forwarded to the
16212                *    {@link ng.$exceptionHandler $exceptionHandler} service.
16213                * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16214                *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16215                *
16216                *
16217                * @param {(string|function())=} exp An angular expression to be executed.
16218                *
16219                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16220                *    - `function(scope)`: execute the function with current `scope` parameter.
16221                *
16222                * @returns {*} The result of evaluating the expression.
16223                */
16224               $apply: function(expr) {
16225                 try {
16226                   beginPhase('$apply');
16227                   try {
16228                     return this.$eval(expr);
16229                   } finally {
16230                     clearPhase();
16231                   }
16232                 } catch (e) {
16233                   $exceptionHandler(e);
16234                 } finally {
16235                   try {
16236                     $rootScope.$digest();
16237                   } catch (e) {
16238                     $exceptionHandler(e);
16239                     throw e;
16240                   }
16241                 }
16242               },
16243
16244               /**
16245                * @ngdoc method
16246                * @name $rootScope.Scope#$applyAsync
16247                * @kind function
16248                *
16249                * @description
16250                * Schedule the invocation of $apply to occur at a later time. The actual time difference
16251                * varies across browsers, but is typically around ~10 milliseconds.
16252                *
16253                * This can be used to queue up multiple expressions which need to be evaluated in the same
16254                * digest.
16255                *
16256                * @param {(string|function())=} exp An angular expression to be executed.
16257                *
16258                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16259                *    - `function(scope)`: execute the function with current `scope` parameter.
16260                */
16261               $applyAsync: function(expr) {
16262                 var scope = this;
16263                 expr && applyAsyncQueue.push($applyAsyncExpression);
16264                 scheduleApplyAsync();
16265
16266                 function $applyAsyncExpression() {
16267                   scope.$eval(expr);
16268                 }
16269               },
16270
16271               /**
16272                * @ngdoc method
16273                * @name $rootScope.Scope#$on
16274                * @kind function
16275                *
16276                * @description
16277                * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16278                * discussion of event life cycle.
16279                *
16280                * The event listener function format is: `function(event, args...)`. The `event` object
16281                * passed into the listener has the following attributes:
16282                *
16283                *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16284                *     `$broadcast`-ed.
16285                *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16286                *     event propagates through the scope hierarchy, this property is set to null.
16287                *   - `name` - `{string}`: name of the event.
16288                *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16289                *     further event propagation (available only for events that were `$emit`-ed).
16290                *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16291                *     to true.
16292                *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16293                *
16294                * @param {string} name Event name to listen on.
16295                * @param {function(event, ...args)} listener Function to call when the event is emitted.
16296                * @returns {function()} Returns a deregistration function for this listener.
16297                */
16298               $on: function(name, listener) {
16299                 var namedListeners = this.$$listeners[name];
16300                 if (!namedListeners) {
16301                   this.$$listeners[name] = namedListeners = [];
16302                 }
16303                 namedListeners.push(listener);
16304
16305                 var current = this;
16306                 do {
16307                   if (!current.$$listenerCount[name]) {
16308                     current.$$listenerCount[name] = 0;
16309                   }
16310                   current.$$listenerCount[name]++;
16311                 } while ((current = current.$parent));
16312
16313                 var self = this;
16314                 return function() {
16315                   var indexOfListener = namedListeners.indexOf(listener);
16316                   if (indexOfListener !== -1) {
16317                     namedListeners[indexOfListener] = null;
16318                     decrementListenerCount(self, 1, name);
16319                   }
16320                 };
16321               },
16322
16323
16324               /**
16325                * @ngdoc method
16326                * @name $rootScope.Scope#$emit
16327                * @kind function
16328                *
16329                * @description
16330                * Dispatches an event `name` upwards through the scope hierarchy notifying the
16331                * registered {@link ng.$rootScope.Scope#$on} listeners.
16332                *
16333                * The event life cycle starts at the scope on which `$emit` was called. All
16334                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16335                * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16336                * registered listeners along the way. The event will stop propagating if one of the listeners
16337                * cancels it.
16338                *
16339                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16340                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16341                *
16342                * @param {string} name Event name to emit.
16343                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16344                * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16345                */
16346               $emit: function(name, args) {
16347                 var empty = [],
16348                     namedListeners,
16349                     scope = this,
16350                     stopPropagation = false,
16351                     event = {
16352                       name: name,
16353                       targetScope: scope,
16354                       stopPropagation: function() {stopPropagation = true;},
16355                       preventDefault: function() {
16356                         event.defaultPrevented = true;
16357                       },
16358                       defaultPrevented: false
16359                     },
16360                     listenerArgs = concat([event], arguments, 1),
16361                     i, length;
16362
16363                 do {
16364                   namedListeners = scope.$$listeners[name] || empty;
16365                   event.currentScope = scope;
16366                   for (i = 0, length = namedListeners.length; i < length; i++) {
16367
16368                     // if listeners were deregistered, defragment the array
16369                     if (!namedListeners[i]) {
16370                       namedListeners.splice(i, 1);
16371                       i--;
16372                       length--;
16373                       continue;
16374                     }
16375                     try {
16376                       //allow all listeners attached to the current scope to run
16377                       namedListeners[i].apply(null, listenerArgs);
16378                     } catch (e) {
16379                       $exceptionHandler(e);
16380                     }
16381                   }
16382                   //if any listener on the current scope stops propagation, prevent bubbling
16383                   if (stopPropagation) {
16384                     event.currentScope = null;
16385                     return event;
16386                   }
16387                   //traverse upwards
16388                   scope = scope.$parent;
16389                 } while (scope);
16390
16391                 event.currentScope = null;
16392
16393                 return event;
16394               },
16395
16396
16397               /**
16398                * @ngdoc method
16399                * @name $rootScope.Scope#$broadcast
16400                * @kind function
16401                *
16402                * @description
16403                * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16404                * registered {@link ng.$rootScope.Scope#$on} listeners.
16405                *
16406                * The event life cycle starts at the scope on which `$broadcast` was called. All
16407                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16408                * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16409                * scope and calls all registered listeners along the way. The event cannot be canceled.
16410                *
16411                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16412                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16413                *
16414                * @param {string} name Event name to broadcast.
16415                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16416                * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16417                */
16418               $broadcast: function(name, args) {
16419                 var target = this,
16420                     current = target,
16421                     next = target,
16422                     event = {
16423                       name: name,
16424                       targetScope: target,
16425                       preventDefault: function() {
16426                         event.defaultPrevented = true;
16427                       },
16428                       defaultPrevented: false
16429                     };
16430
16431                 if (!target.$$listenerCount[name]) return event;
16432
16433                 var listenerArgs = concat([event], arguments, 1),
16434                     listeners, i, length;
16435
16436                 //down while you can, then up and next sibling or up and next sibling until back at root
16437                 while ((current = next)) {
16438                   event.currentScope = current;
16439                   listeners = current.$$listeners[name] || [];
16440                   for (i = 0, length = listeners.length; i < length; i++) {
16441                     // if listeners were deregistered, defragment the array
16442                     if (!listeners[i]) {
16443                       listeners.splice(i, 1);
16444                       i--;
16445                       length--;
16446                       continue;
16447                     }
16448
16449                     try {
16450                       listeners[i].apply(null, listenerArgs);
16451                     } catch (e) {
16452                       $exceptionHandler(e);
16453                     }
16454                   }
16455
16456                   // Insanity Warning: scope depth-first traversal
16457                   // yes, this code is a bit crazy, but it works and we have tests to prove it!
16458                   // this piece should be kept in sync with the traversal in $digest
16459                   // (though it differs due to having the extra check for $$listenerCount)
16460                   if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16461                       (current !== target && current.$$nextSibling)))) {
16462                     while (current !== target && !(next = current.$$nextSibling)) {
16463                       current = current.$parent;
16464                     }
16465                   }
16466                 }
16467
16468                 event.currentScope = null;
16469                 return event;
16470               }
16471             };
16472
16473             var $rootScope = new Scope();
16474
16475             //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16476             var asyncQueue = $rootScope.$$asyncQueue = [];
16477             var postDigestQueue = $rootScope.$$postDigestQueue = [];
16478             var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16479
16480             return $rootScope;
16481
16482
16483             function beginPhase(phase) {
16484               if ($rootScope.$$phase) {
16485                 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16486               }
16487
16488               $rootScope.$$phase = phase;
16489             }
16490
16491             function clearPhase() {
16492               $rootScope.$$phase = null;
16493             }
16494
16495             function incrementWatchersCount(current, count) {
16496               do {
16497                 current.$$watchersCount += count;
16498               } while ((current = current.$parent));
16499             }
16500
16501             function decrementListenerCount(current, count, name) {
16502               do {
16503                 current.$$listenerCount[name] -= count;
16504
16505                 if (current.$$listenerCount[name] === 0) {
16506                   delete current.$$listenerCount[name];
16507                 }
16508               } while ((current = current.$parent));
16509             }
16510
16511             /**
16512              * function used as an initial value for watchers.
16513              * because it's unique we can easily tell it apart from other values
16514              */
16515             function initWatchVal() {}
16516
16517             function flushApplyAsync() {
16518               while (applyAsyncQueue.length) {
16519                 try {
16520                   applyAsyncQueue.shift()();
16521                 } catch (e) {
16522                   $exceptionHandler(e);
16523                 }
16524               }
16525               applyAsyncId = null;
16526             }
16527
16528             function scheduleApplyAsync() {
16529               if (applyAsyncId === null) {
16530                 applyAsyncId = $browser.defer(function() {
16531                   $rootScope.$apply(flushApplyAsync);
16532                 });
16533               }
16534             }
16535           }];
16536         }
16537
16538         /**
16539          * @description
16540          * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16541          */
16542         function $$SanitizeUriProvider() {
16543           var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16544             imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16545
16546           /**
16547            * @description
16548            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16549            * urls during a[href] sanitization.
16550            *
16551            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16552            *
16553            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16554            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16555            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16556            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16557            *
16558            * @param {RegExp=} regexp New regexp to whitelist urls with.
16559            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16560            *    chaining otherwise.
16561            */
16562           this.aHrefSanitizationWhitelist = function(regexp) {
16563             if (isDefined(regexp)) {
16564               aHrefSanitizationWhitelist = regexp;
16565               return this;
16566             }
16567             return aHrefSanitizationWhitelist;
16568           };
16569
16570
16571           /**
16572            * @description
16573            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16574            * urls during img[src] sanitization.
16575            *
16576            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16577            *
16578            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16579            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16580            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16581            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16582            *
16583            * @param {RegExp=} regexp New regexp to whitelist urls with.
16584            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16585            *    chaining otherwise.
16586            */
16587           this.imgSrcSanitizationWhitelist = function(regexp) {
16588             if (isDefined(regexp)) {
16589               imgSrcSanitizationWhitelist = regexp;
16590               return this;
16591             }
16592             return imgSrcSanitizationWhitelist;
16593           };
16594
16595           this.$get = function() {
16596             return function sanitizeUri(uri, isImage) {
16597               var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16598               var normalizedVal;
16599               normalizedVal = urlResolve(uri).href;
16600               if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16601                 return 'unsafe:' + normalizedVal;
16602               }
16603               return uri;
16604             };
16605           };
16606         }
16607
16608         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16609          *     Any commits to this file should be reviewed with security in mind.  *
16610          *   Changes to this file can potentially create security vulnerabilities. *
16611          *          An approval from 2 Core members with history of modifying      *
16612          *                         this file is required.                          *
16613          *                                                                         *
16614          *  Does the change somehow allow for arbitrary javascript to be executed? *
16615          *    Or allows for someone to change the prototype of built-in objects?   *
16616          *     Or gives undesired access to variables likes document or window?    *
16617          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16618
16619         var $sceMinErr = minErr('$sce');
16620
16621         var SCE_CONTEXTS = {
16622           HTML: 'html',
16623           CSS: 'css',
16624           URL: 'url',
16625           // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16626           // url.  (e.g. ng-include, script src, templateUrl)
16627           RESOURCE_URL: 'resourceUrl',
16628           JS: 'js'
16629         };
16630
16631         // Helper functions follow.
16632
16633         function adjustMatcher(matcher) {
16634           if (matcher === 'self') {
16635             return matcher;
16636           } else if (isString(matcher)) {
16637             // Strings match exactly except for 2 wildcards - '*' and '**'.
16638             // '*' matches any character except those from the set ':/.?&'.
16639             // '**' matches any character (like .* in a RegExp).
16640             // More than 2 *'s raises an error as it's ill defined.
16641             if (matcher.indexOf('***') > -1) {
16642               throw $sceMinErr('iwcard',
16643                   'Illegal sequence *** in string matcher.  String: {0}', matcher);
16644             }
16645             matcher = escapeForRegexp(matcher).
16646                           replace('\\*\\*', '.*').
16647                           replace('\\*', '[^:/.?&;]*');
16648             return new RegExp('^' + matcher + '$');
16649           } else if (isRegExp(matcher)) {
16650             // The only other type of matcher allowed is a Regexp.
16651             // Match entire URL / disallow partial matches.
16652             // Flags are reset (i.e. no global, ignoreCase or multiline)
16653             return new RegExp('^' + matcher.source + '$');
16654           } else {
16655             throw $sceMinErr('imatcher',
16656                 'Matchers may only be "self", string patterns or RegExp objects');
16657           }
16658         }
16659
16660
16661         function adjustMatchers(matchers) {
16662           var adjustedMatchers = [];
16663           if (isDefined(matchers)) {
16664             forEach(matchers, function(matcher) {
16665               adjustedMatchers.push(adjustMatcher(matcher));
16666             });
16667           }
16668           return adjustedMatchers;
16669         }
16670
16671
16672         /**
16673          * @ngdoc service
16674          * @name $sceDelegate
16675          * @kind function
16676          *
16677          * @description
16678          *
16679          * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16680          * Contextual Escaping (SCE)} services to AngularJS.
16681          *
16682          * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16683          * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16684          * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16685          * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16686          * work because `$sce` delegates to `$sceDelegate` for these operations.
16687          *
16688          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16689          *
16690          * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16691          * can override it completely to change the behavior of `$sce`, the common case would
16692          * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16693          * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16694          * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16695          * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16696          * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16697          */
16698
16699         /**
16700          * @ngdoc provider
16701          * @name $sceDelegateProvider
16702          * @description
16703          *
16704          * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16705          * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16706          * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16707          * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16708          * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16709          *
16710          * For the general details about this service in Angular, read the main page for {@link ng.$sce
16711          * Strict Contextual Escaping (SCE)}.
16712          *
16713          * **Example**:  Consider the following case. <a name="example"></a>
16714          *
16715          * - your app is hosted at url `http://myapp.example.com/`
16716          * - but some of your templates are hosted on other domains you control such as
16717          *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16718          * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16719          *
16720          * Here is what a secure configuration for this scenario might look like:
16721          *
16722          * ```
16723          *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16724          *    $sceDelegateProvider.resourceUrlWhitelist([
16725          *      // Allow same origin resource loads.
16726          *      'self',
16727          *      // Allow loading from our assets domain.  Notice the difference between * and **.
16728          *      'http://srv*.assets.example.com/**'
16729          *    ]);
16730          *
16731          *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16732          *    $sceDelegateProvider.resourceUrlBlacklist([
16733          *      'http://myapp.example.com/clickThru**'
16734          *    ]);
16735          *  });
16736          * ```
16737          */
16738
16739         function $SceDelegateProvider() {
16740           this.SCE_CONTEXTS = SCE_CONTEXTS;
16741
16742           // Resource URLs can also be trusted by policy.
16743           var resourceUrlWhitelist = ['self'],
16744               resourceUrlBlacklist = [];
16745
16746           /**
16747            * @ngdoc method
16748            * @name $sceDelegateProvider#resourceUrlWhitelist
16749            * @kind function
16750            *
16751            * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16752            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16753            *     changes to the array are ignored.
16754            *
16755            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16756            *     allowed in this array.
16757            *
16758            *     Note: **an empty whitelist array will block all URLs**!
16759            *
16760            * @return {Array} the currently set whitelist array.
16761            *
16762            * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16763            * same origin resource requests.
16764            *
16765            * @description
16766            * Sets/Gets the whitelist of trusted resource URLs.
16767            */
16768           this.resourceUrlWhitelist = function(value) {
16769             if (arguments.length) {
16770               resourceUrlWhitelist = adjustMatchers(value);
16771             }
16772             return resourceUrlWhitelist;
16773           };
16774
16775           /**
16776            * @ngdoc method
16777            * @name $sceDelegateProvider#resourceUrlBlacklist
16778            * @kind function
16779            *
16780            * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16781            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16782            *     changes to the array are ignored.
16783            *
16784            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16785            *     allowed in this array.
16786            *
16787            *     The typical usage for the blacklist is to **block
16788            *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16789            *     these would otherwise be trusted but actually return content from the redirected domain.
16790            *
16791            *     Finally, **the blacklist overrides the whitelist** and has the final say.
16792            *
16793            * @return {Array} the currently set blacklist array.
16794            *
16795            * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16796            * is no blacklist.)
16797            *
16798            * @description
16799            * Sets/Gets the blacklist of trusted resource URLs.
16800            */
16801
16802           this.resourceUrlBlacklist = function(value) {
16803             if (arguments.length) {
16804               resourceUrlBlacklist = adjustMatchers(value);
16805             }
16806             return resourceUrlBlacklist;
16807           };
16808
16809           this.$get = ['$injector', function($injector) {
16810
16811             var htmlSanitizer = function htmlSanitizer(html) {
16812               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16813             };
16814
16815             if ($injector.has('$sanitize')) {
16816               htmlSanitizer = $injector.get('$sanitize');
16817             }
16818
16819
16820             function matchUrl(matcher, parsedUrl) {
16821               if (matcher === 'self') {
16822                 return urlIsSameOrigin(parsedUrl);
16823               } else {
16824                 // definitely a regex.  See adjustMatchers()
16825                 return !!matcher.exec(parsedUrl.href);
16826               }
16827             }
16828
16829             function isResourceUrlAllowedByPolicy(url) {
16830               var parsedUrl = urlResolve(url.toString());
16831               var i, n, allowed = false;
16832               // Ensure that at least one item from the whitelist allows this url.
16833               for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16834                 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16835                   allowed = true;
16836                   break;
16837                 }
16838               }
16839               if (allowed) {
16840                 // Ensure that no item from the blacklist blocked this url.
16841                 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16842                   if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16843                     allowed = false;
16844                     break;
16845                   }
16846                 }
16847               }
16848               return allowed;
16849             }
16850
16851             function generateHolderType(Base) {
16852               var holderType = function TrustedValueHolderType(trustedValue) {
16853                 this.$$unwrapTrustedValue = function() {
16854                   return trustedValue;
16855                 };
16856               };
16857               if (Base) {
16858                 holderType.prototype = new Base();
16859               }
16860               holderType.prototype.valueOf = function sceValueOf() {
16861                 return this.$$unwrapTrustedValue();
16862               };
16863               holderType.prototype.toString = function sceToString() {
16864                 return this.$$unwrapTrustedValue().toString();
16865               };
16866               return holderType;
16867             }
16868
16869             var trustedValueHolderBase = generateHolderType(),
16870                 byType = {};
16871
16872             byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16873             byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16874             byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16875             byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16876             byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16877
16878             /**
16879              * @ngdoc method
16880              * @name $sceDelegate#trustAs
16881              *
16882              * @description
16883              * Returns an object that is trusted by angular for use in specified strict
16884              * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16885              * attribute interpolation, any dom event binding attribute interpolation
16886              * such as for onclick,  etc.) that uses the provided value.
16887              * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16888              *
16889              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16890              *   resourceUrl, html, js and css.
16891              * @param {*} value The value that that should be considered trusted/safe.
16892              * @returns {*} A value that can be used to stand in for the provided `value` in places
16893              * where Angular expects a $sce.trustAs() return value.
16894              */
16895             function trustAs(type, trustedValue) {
16896               var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16897               if (!Constructor) {
16898                 throw $sceMinErr('icontext',
16899                     'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16900                     type, trustedValue);
16901               }
16902               if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16903                 return trustedValue;
16904               }
16905               // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16906               // mutable objects, we ensure here that the value passed in is actually a string.
16907               if (typeof trustedValue !== 'string') {
16908                 throw $sceMinErr('itype',
16909                     'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16910                     type);
16911               }
16912               return new Constructor(trustedValue);
16913             }
16914
16915             /**
16916              * @ngdoc method
16917              * @name $sceDelegate#valueOf
16918              *
16919              * @description
16920              * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16921              * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16922              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16923              *
16924              * If the passed parameter is not a value that had been returned by {@link
16925              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16926              *
16927              * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16928              *      call or anything else.
16929              * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16930              *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16931              *     `value` unchanged.
16932              */
16933             function valueOf(maybeTrusted) {
16934               if (maybeTrusted instanceof trustedValueHolderBase) {
16935                 return maybeTrusted.$$unwrapTrustedValue();
16936               } else {
16937                 return maybeTrusted;
16938               }
16939             }
16940
16941             /**
16942              * @ngdoc method
16943              * @name $sceDelegate#getTrusted
16944              *
16945              * @description
16946              * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16947              * returns the originally supplied value if the queried context type is a supertype of the
16948              * created type.  If this condition isn't satisfied, throws an exception.
16949              *
16950              * @param {string} type The kind of context in which this value is to be used.
16951              * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16952              *     `$sceDelegate.trustAs`} call.
16953              * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16954              *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16955              */
16956             function getTrusted(type, maybeTrusted) {
16957               if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16958                 return maybeTrusted;
16959               }
16960               var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16961               if (constructor && maybeTrusted instanceof constructor) {
16962                 return maybeTrusted.$$unwrapTrustedValue();
16963               }
16964               // If we get here, then we may only take one of two actions.
16965               // 1. sanitize the value for the requested type, or
16966               // 2. throw an exception.
16967               if (type === SCE_CONTEXTS.RESOURCE_URL) {
16968                 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16969                   return maybeTrusted;
16970                 } else {
16971                   throw $sceMinErr('insecurl',
16972                       'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16973                       maybeTrusted.toString());
16974                 }
16975               } else if (type === SCE_CONTEXTS.HTML) {
16976                 return htmlSanitizer(maybeTrusted);
16977               }
16978               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16979             }
16980
16981             return { trustAs: trustAs,
16982                      getTrusted: getTrusted,
16983                      valueOf: valueOf };
16984           }];
16985         }
16986
16987
16988         /**
16989          * @ngdoc provider
16990          * @name $sceProvider
16991          * @description
16992          *
16993          * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16994          * -   enable/disable Strict Contextual Escaping (SCE) in a module
16995          * -   override the default implementation with a custom delegate
16996          *
16997          * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16998          */
16999
17000         /* jshint maxlen: false*/
17001
17002         /**
17003          * @ngdoc service
17004          * @name $sce
17005          * @kind function
17006          *
17007          * @description
17008          *
17009          * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17010          *
17011          * # Strict Contextual Escaping
17012          *
17013          * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17014          * contexts to result in a value that is marked as safe to use for that context.  One example of
17015          * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17016          * to these contexts as privileged or SCE contexts.
17017          *
17018          * As of version 1.2, Angular ships with SCE enabled by default.
17019          *
17020          * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17021          * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17022          * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17023          * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17024          * to the top of your HTML document.
17025          *
17026          * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17027          * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17028          *
17029          * Here's an example of a binding in a privileged context:
17030          *
17031          * ```
17032          * <input ng-model="userHtml" aria-label="User input">
17033          * <div ng-bind-html="userHtml"></div>
17034          * ```
17035          *
17036          * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17037          * disabled, this application allows the user to render arbitrary HTML into the DIV.
17038          * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17039          * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17040          * security vulnerabilities.)
17041          *
17042          * For the case of HTML, you might use a library, either on the client side, or on the server side,
17043          * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17044          *
17045          * How would you ensure that every place that used these types of bindings was bound to a value that
17046          * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17047          * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17048          * properties/fields and forgot to update the binding to the sanitized value?
17049          *
17050          * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17051          * determine that something explicitly says it's safe to use a value for binding in that
17052          * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17053          * for those values that you can easily tell are safe - because they were received from your server,
17054          * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17055          * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17056          * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17057          *
17058          * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17059          * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17060          * obtain values that will be accepted by SCE / privileged contexts.
17061          *
17062          *
17063          * ## How does it work?
17064          *
17065          * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17066          * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17067          * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17068          * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17069          *
17070          * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17071          * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17072          * simplified):
17073          *
17074          * ```
17075          * var ngBindHtmlDirective = ['$sce', function($sce) {
17076          *   return function(scope, element, attr) {
17077          *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17078          *       element.html(value || '');
17079          *     });
17080          *   };
17081          * }];
17082          * ```
17083          *
17084          * ## Impact on loading templates
17085          *
17086          * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17087          * `templateUrl`'s specified by {@link guide/directive directives}.
17088          *
17089          * By default, Angular only loads templates from the same domain and protocol as the application
17090          * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17091          * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17092          * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17093          * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17094          *
17095          * *Please note*:
17096          * The browser's
17097          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17098          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17099          * policy apply in addition to this and may further restrict whether the template is successfully
17100          * loaded.  This means that without the right CORS policy, loading templates from a different domain
17101          * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17102          * browsers.
17103          *
17104          * ## This feels like too much overhead
17105          *
17106          * It's important to remember that SCE only applies to interpolation expressions.
17107          *
17108          * If your expressions are constant literals, they're automatically trusted and you don't need to
17109          * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17110          * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17111          *
17112          * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17113          * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17114          *
17115          * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17116          * templates in `ng-include` from your application's domain without having to even know about SCE.
17117          * It blocks loading templates from other domains or loading templates over http from an https
17118          * served document.  You can change these by setting your own custom {@link
17119          * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17120          * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17121          *
17122          * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17123          * application that's secure and can be audited to verify that with much more ease than bolting
17124          * security onto an application later.
17125          *
17126          * <a name="contexts"></a>
17127          * ## What trusted context types are supported?
17128          *
17129          * | Context             | Notes          |
17130          * |---------------------|----------------|
17131          * | `$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. |
17132          * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17133          * | `$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. |
17134          * | `$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. |
17135          * | `$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. |
17136          *
17137          * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17138          *
17139          *  Each element in these arrays must be one of the following:
17140          *
17141          *  - **'self'**
17142          *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17143          *      domain** as the application document using the **same protocol**.
17144          *  - **String** (except the special value `'self'`)
17145          *    - The string is matched against the full *normalized / absolute URL* of the resource
17146          *      being tested (substring matches are not good enough.)
17147          *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17148          *      match themselves.
17149          *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17150          *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17151          *      in a whitelist.
17152          *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17153          *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17154          *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17155          *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17156          *      http://foo.example.com/templates/**).
17157          *  - **RegExp** (*see caveat below*)
17158          *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17159          *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17160          *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17161          *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17162          *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17163          *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17164          *      is highly recommended to use the string patterns and only fall back to regular expressions
17165          *      as a last resort.
17166          *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17167          *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17168          *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17169          *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17170          *    - If you are generating your JavaScript from some other templating engine (not
17171          *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17172          *      remember to escape your regular expression (and be aware that you might need more than
17173          *      one level of escaping depending on your templating engine and the way you interpolated
17174          *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17175          *      enough before coding your own.  E.g. Ruby has
17176          *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17177          *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17178          *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17179          *      Closure library's [goog.string.regExpEscape(s)](
17180          *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17181          *
17182          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17183          *
17184          * ## Show me an example using SCE.
17185          *
17186          * <example module="mySceApp" deps="angular-sanitize.js">
17187          * <file name="index.html">
17188          *   <div ng-controller="AppController as myCtrl">
17189          *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17190          *     <b>User comments</b><br>
17191          *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17192          *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17193          *     exploit.
17194          *     <div class="well">
17195          *       <div ng-repeat="userComment in myCtrl.userComments">
17196          *         <b>{{userComment.name}}</b>:
17197          *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17198          *         <br>
17199          *       </div>
17200          *     </div>
17201          *   </div>
17202          * </file>
17203          *
17204          * <file name="script.js">
17205          *   angular.module('mySceApp', ['ngSanitize'])
17206          *     .controller('AppController', ['$http', '$templateCache', '$sce',
17207          *       function($http, $templateCache, $sce) {
17208          *         var self = this;
17209          *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17210          *           self.userComments = userComments;
17211          *         });
17212          *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17213          *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17214          *             'sanitization.&quot;">Hover over this text.</span>');
17215          *       }]);
17216          * </file>
17217          *
17218          * <file name="test_data.json">
17219          * [
17220          *   { "name": "Alice",
17221          *     "htmlComment":
17222          *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17223          *   },
17224          *   { "name": "Bob",
17225          *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17226          *   }
17227          * ]
17228          * </file>
17229          *
17230          * <file name="protractor.js" type="protractor">
17231          *   describe('SCE doc demo', function() {
17232          *     it('should sanitize untrusted values', function() {
17233          *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17234          *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17235          *     });
17236          *
17237          *     it('should NOT sanitize explicitly trusted values', function() {
17238          *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17239          *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17240          *           'sanitization.&quot;">Hover over this text.</span>');
17241          *     });
17242          *   });
17243          * </file>
17244          * </example>
17245          *
17246          *
17247          *
17248          * ## Can I disable SCE completely?
17249          *
17250          * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17251          * for little coding overhead.  It will be much harder to take an SCE disabled application and
17252          * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17253          * for cases where you have a lot of existing code that was written before SCE was introduced and
17254          * you're migrating them a module at a time.
17255          *
17256          * That said, here's how you can completely disable SCE:
17257          *
17258          * ```
17259          * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17260          *   // Completely disable SCE.  For demonstration purposes only!
17261          *   // Do not use in new projects.
17262          *   $sceProvider.enabled(false);
17263          * });
17264          * ```
17265          *
17266          */
17267         /* jshint maxlen: 100 */
17268
17269         function $SceProvider() {
17270           var enabled = true;
17271
17272           /**
17273            * @ngdoc method
17274            * @name $sceProvider#enabled
17275            * @kind function
17276            *
17277            * @param {boolean=} value If provided, then enables/disables SCE.
17278            * @return {boolean} true if SCE is enabled, false otherwise.
17279            *
17280            * @description
17281            * Enables/disables SCE and returns the current value.
17282            */
17283           this.enabled = function(value) {
17284             if (arguments.length) {
17285               enabled = !!value;
17286             }
17287             return enabled;
17288           };
17289
17290
17291           /* Design notes on the default implementation for SCE.
17292            *
17293            * The API contract for the SCE delegate
17294            * -------------------------------------
17295            * The SCE delegate object must provide the following 3 methods:
17296            *
17297            * - trustAs(contextEnum, value)
17298            *     This method is used to tell the SCE service that the provided value is OK to use in the
17299            *     contexts specified by contextEnum.  It must return an object that will be accepted by
17300            *     getTrusted() for a compatible contextEnum and return this value.
17301            *
17302            * - valueOf(value)
17303            *     For values that were not produced by trustAs(), return them as is.  For values that were
17304            *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17305            *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17306            *     such a value.
17307            *
17308            * - getTrusted(contextEnum, value)
17309            *     This function should return the a value that is safe to use in the context specified by
17310            *     contextEnum or throw and exception otherwise.
17311            *
17312            * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17313            * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17314            * instance, an implementation could maintain a registry of all trusted objects by context.  In
17315            * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17316            * return the same object passed in if it was found in the registry under a compatible context or
17317            * throw an exception otherwise.  An implementation might only wrap values some of the time based
17318            * on some criteria.  getTrusted() might return a value and not throw an exception for special
17319            * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17320            *
17321            *
17322            * A note on the inheritance model for SCE contexts
17323            * ------------------------------------------------
17324            * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17325            * is purely an implementation details.
17326            *
17327            * The contract is simply this:
17328            *
17329            *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17330            *     will also succeed.
17331            *
17332            * Inheritance happens to capture this in a natural way.  In some future, we
17333            * may not use inheritance anymore.  That is OK because no code outside of
17334            * sce.js and sceSpecs.js would need to be aware of this detail.
17335            */
17336
17337           this.$get = ['$parse', '$sceDelegate', function(
17338                         $parse,   $sceDelegate) {
17339             // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17340             // the "expression(javascript expression)" syntax which is insecure.
17341             if (enabled && msie < 8) {
17342               throw $sceMinErr('iequirks',
17343                 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17344                 'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17345                 'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17346             }
17347
17348             var sce = shallowCopy(SCE_CONTEXTS);
17349
17350             /**
17351              * @ngdoc method
17352              * @name $sce#isEnabled
17353              * @kind function
17354              *
17355              * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17356              * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17357              *
17358              * @description
17359              * Returns a boolean indicating if SCE is enabled.
17360              */
17361             sce.isEnabled = function() {
17362               return enabled;
17363             };
17364             sce.trustAs = $sceDelegate.trustAs;
17365             sce.getTrusted = $sceDelegate.getTrusted;
17366             sce.valueOf = $sceDelegate.valueOf;
17367
17368             if (!enabled) {
17369               sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17370               sce.valueOf = identity;
17371             }
17372
17373             /**
17374              * @ngdoc method
17375              * @name $sce#parseAs
17376              *
17377              * @description
17378              * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17379              * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17380              * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17381              * *result*)}
17382              *
17383              * @param {string} type The kind of SCE context in which this result will be used.
17384              * @param {string} expression String expression to compile.
17385              * @returns {function(context, locals)} a function which represents the compiled expression:
17386              *
17387              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17388              *      are evaluated against (typically a scope object).
17389              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17390              *      `context`.
17391              */
17392             sce.parseAs = function sceParseAs(type, expr) {
17393               var parsed = $parse(expr);
17394               if (parsed.literal && parsed.constant) {
17395                 return parsed;
17396               } else {
17397                 return $parse(expr, function(value) {
17398                   return sce.getTrusted(type, value);
17399                 });
17400               }
17401             };
17402
17403             /**
17404              * @ngdoc method
17405              * @name $sce#trustAs
17406              *
17407              * @description
17408              * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17409              * returns an object that is trusted by angular for use in specified strict contextual
17410              * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17411              * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17412              * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17413              * escaping.
17414              *
17415              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17416              *   resourceUrl, html, js and css.
17417              * @param {*} value The value that that should be considered trusted/safe.
17418              * @returns {*} A value that can be used to stand in for the provided `value` in places
17419              * where Angular expects a $sce.trustAs() return value.
17420              */
17421
17422             /**
17423              * @ngdoc method
17424              * @name $sce#trustAsHtml
17425              *
17426              * @description
17427              * Shorthand method.  `$sce.trustAsHtml(value)` →
17428              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17429              *
17430              * @param {*} value The value to trustAs.
17431              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17432              *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17433              *     only accept expressions that are either literal constants or are the
17434              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17435              */
17436
17437             /**
17438              * @ngdoc method
17439              * @name $sce#trustAsUrl
17440              *
17441              * @description
17442              * Shorthand method.  `$sce.trustAsUrl(value)` →
17443              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17444              *
17445              * @param {*} value The value to trustAs.
17446              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17447              *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17448              *     only accept expressions that are either literal constants or are the
17449              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17450              */
17451
17452             /**
17453              * @ngdoc method
17454              * @name $sce#trustAsResourceUrl
17455              *
17456              * @description
17457              * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17458              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17459              *
17460              * @param {*} value The value to trustAs.
17461              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17462              *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17463              *     only accept expressions that are either literal constants or are the return
17464              *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17465              */
17466
17467             /**
17468              * @ngdoc method
17469              * @name $sce#trustAsJs
17470              *
17471              * @description
17472              * Shorthand method.  `$sce.trustAsJs(value)` →
17473              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17474              *
17475              * @param {*} value The value to trustAs.
17476              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17477              *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17478              *     only accept expressions that are either literal constants or are the
17479              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17480              */
17481
17482             /**
17483              * @ngdoc method
17484              * @name $sce#getTrusted
17485              *
17486              * @description
17487              * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17488              * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17489              * originally supplied value if the queried context type is a supertype of the created type.
17490              * If this condition isn't satisfied, throws an exception.
17491              *
17492              * @param {string} type The kind of context in which this value is to be used.
17493              * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17494              *                         call.
17495              * @returns {*} The value the was originally provided to
17496              *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17497              *              Otherwise, throws an exception.
17498              */
17499
17500             /**
17501              * @ngdoc method
17502              * @name $sce#getTrustedHtml
17503              *
17504              * @description
17505              * Shorthand method.  `$sce.getTrustedHtml(value)` →
17506              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17507              *
17508              * @param {*} value The value to pass to `$sce.getTrusted`.
17509              * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17510              */
17511
17512             /**
17513              * @ngdoc method
17514              * @name $sce#getTrustedCss
17515              *
17516              * @description
17517              * Shorthand method.  `$sce.getTrustedCss(value)` →
17518              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17519              *
17520              * @param {*} value The value to pass to `$sce.getTrusted`.
17521              * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17522              */
17523
17524             /**
17525              * @ngdoc method
17526              * @name $sce#getTrustedUrl
17527              *
17528              * @description
17529              * Shorthand method.  `$sce.getTrustedUrl(value)` →
17530              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17531              *
17532              * @param {*} value The value to pass to `$sce.getTrusted`.
17533              * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17534              */
17535
17536             /**
17537              * @ngdoc method
17538              * @name $sce#getTrustedResourceUrl
17539              *
17540              * @description
17541              * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17542              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17543              *
17544              * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17545              * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17546              */
17547
17548             /**
17549              * @ngdoc method
17550              * @name $sce#getTrustedJs
17551              *
17552              * @description
17553              * Shorthand method.  `$sce.getTrustedJs(value)` →
17554              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17555              *
17556              * @param {*} value The value to pass to `$sce.getTrusted`.
17557              * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17558              */
17559
17560             /**
17561              * @ngdoc method
17562              * @name $sce#parseAsHtml
17563              *
17564              * @description
17565              * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17566              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17567              *
17568              * @param {string} expression String expression to compile.
17569              * @returns {function(context, locals)} a function which represents the compiled expression:
17570              *
17571              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17572              *      are evaluated against (typically a scope object).
17573              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17574              *      `context`.
17575              */
17576
17577             /**
17578              * @ngdoc method
17579              * @name $sce#parseAsCss
17580              *
17581              * @description
17582              * Shorthand method.  `$sce.parseAsCss(value)` →
17583              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17584              *
17585              * @param {string} expression String expression to compile.
17586              * @returns {function(context, locals)} a function which represents the compiled expression:
17587              *
17588              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17589              *      are evaluated against (typically a scope object).
17590              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17591              *      `context`.
17592              */
17593
17594             /**
17595              * @ngdoc method
17596              * @name $sce#parseAsUrl
17597              *
17598              * @description
17599              * Shorthand method.  `$sce.parseAsUrl(value)` →
17600              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17601              *
17602              * @param {string} expression String expression to compile.
17603              * @returns {function(context, locals)} a function which represents the compiled expression:
17604              *
17605              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17606              *      are evaluated against (typically a scope object).
17607              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17608              *      `context`.
17609              */
17610
17611             /**
17612              * @ngdoc method
17613              * @name $sce#parseAsResourceUrl
17614              *
17615              * @description
17616              * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17617              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17618              *
17619              * @param {string} expression String expression to compile.
17620              * @returns {function(context, locals)} a function which represents the compiled expression:
17621              *
17622              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17623              *      are evaluated against (typically a scope object).
17624              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17625              *      `context`.
17626              */
17627
17628             /**
17629              * @ngdoc method
17630              * @name $sce#parseAsJs
17631              *
17632              * @description
17633              * Shorthand method.  `$sce.parseAsJs(value)` →
17634              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17635              *
17636              * @param {string} expression String expression to compile.
17637              * @returns {function(context, locals)} a function which represents the compiled expression:
17638              *
17639              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17640              *      are evaluated against (typically a scope object).
17641              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17642              *      `context`.
17643              */
17644
17645             // Shorthand delegations.
17646             var parse = sce.parseAs,
17647                 getTrusted = sce.getTrusted,
17648                 trustAs = sce.trustAs;
17649
17650             forEach(SCE_CONTEXTS, function(enumValue, name) {
17651               var lName = lowercase(name);
17652               sce[camelCase("parse_as_" + lName)] = function(expr) {
17653                 return parse(enumValue, expr);
17654               };
17655               sce[camelCase("get_trusted_" + lName)] = function(value) {
17656                 return getTrusted(enumValue, value);
17657               };
17658               sce[camelCase("trust_as_" + lName)] = function(value) {
17659                 return trustAs(enumValue, value);
17660               };
17661             });
17662
17663             return sce;
17664           }];
17665         }
17666
17667         /**
17668          * !!! This is an undocumented "private" service !!!
17669          *
17670          * @name $sniffer
17671          * @requires $window
17672          * @requires $document
17673          *
17674          * @property {boolean} history Does the browser support html5 history api ?
17675          * @property {boolean} transitions Does the browser support CSS transition events ?
17676          * @property {boolean} animations Does the browser support CSS animation events ?
17677          *
17678          * @description
17679          * This is very simple implementation of testing browser's features.
17680          */
17681         function $SnifferProvider() {
17682           this.$get = ['$window', '$document', function($window, $document) {
17683             var eventSupport = {},
17684                 android =
17685                   toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17686                 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17687                 document = $document[0] || {},
17688                 vendorPrefix,
17689                 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17690                 bodyStyle = document.body && document.body.style,
17691                 transitions = false,
17692                 animations = false,
17693                 match;
17694
17695             if (bodyStyle) {
17696               for (var prop in bodyStyle) {
17697                 if (match = vendorRegex.exec(prop)) {
17698                   vendorPrefix = match[0];
17699                   vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17700                   break;
17701                 }
17702               }
17703
17704               if (!vendorPrefix) {
17705                 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17706               }
17707
17708               transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17709               animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17710
17711               if (android && (!transitions ||  !animations)) {
17712                 transitions = isString(bodyStyle.webkitTransition);
17713                 animations = isString(bodyStyle.webkitAnimation);
17714               }
17715             }
17716
17717
17718             return {
17719               // Android has history.pushState, but it does not update location correctly
17720               // so let's not use the history API at all.
17721               // http://code.google.com/p/android/issues/detail?id=17471
17722               // https://github.com/angular/angular.js/issues/904
17723
17724               // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17725               // so let's not use the history API also
17726               // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17727               // jshint -W018
17728               history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17729               // jshint +W018
17730               hasEvent: function(event) {
17731                 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17732                 // it. In particular the event is not fired when backspace or delete key are pressed or
17733                 // when cut operation is performed.
17734                 // IE10+ implements 'input' event but it erroneously fires under various situations,
17735                 // e.g. when placeholder changes, or a form is focused.
17736                 if (event === 'input' && msie <= 11) return false;
17737
17738                 if (isUndefined(eventSupport[event])) {
17739                   var divElm = document.createElement('div');
17740                   eventSupport[event] = 'on' + event in divElm;
17741                 }
17742
17743                 return eventSupport[event];
17744               },
17745               csp: csp(),
17746               vendorPrefix: vendorPrefix,
17747               transitions: transitions,
17748               animations: animations,
17749               android: android
17750             };
17751           }];
17752         }
17753
17754         var $compileMinErr = minErr('$compile');
17755
17756         /**
17757          * @ngdoc service
17758          * @name $templateRequest
17759          *
17760          * @description
17761          * The `$templateRequest` service runs security checks then downloads the provided template using
17762          * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17763          * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17764          * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17765          * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17766          * when `tpl` is of type string and `$templateCache` has the matching entry.
17767          *
17768          * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17769          * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17770          *
17771          * @return {Promise} a promise for the HTTP response data of the given URL.
17772          *
17773          * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17774          */
17775         function $TemplateRequestProvider() {
17776           this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17777             function handleRequestFn(tpl, ignoreRequestError) {
17778               handleRequestFn.totalPendingRequests++;
17779
17780               // We consider the template cache holds only trusted templates, so
17781               // there's no need to go through whitelisting again for keys that already
17782               // are included in there. This also makes Angular accept any script
17783               // directive, no matter its name. However, we still need to unwrap trusted
17784               // types.
17785               if (!isString(tpl) || !$templateCache.get(tpl)) {
17786                 tpl = $sce.getTrustedResourceUrl(tpl);
17787               }
17788
17789               var transformResponse = $http.defaults && $http.defaults.transformResponse;
17790
17791               if (isArray(transformResponse)) {
17792                 transformResponse = transformResponse.filter(function(transformer) {
17793                   return transformer !== defaultHttpResponseTransform;
17794                 });
17795               } else if (transformResponse === defaultHttpResponseTransform) {
17796                 transformResponse = null;
17797               }
17798
17799               var httpOptions = {
17800                 cache: $templateCache,
17801                 transformResponse: transformResponse
17802               };
17803
17804               return $http.get(tpl, httpOptions)
17805                 ['finally'](function() {
17806                   handleRequestFn.totalPendingRequests--;
17807                 })
17808                 .then(function(response) {
17809                   $templateCache.put(tpl, response.data);
17810                   return response.data;
17811                 }, handleError);
17812
17813               function handleError(resp) {
17814                 if (!ignoreRequestError) {
17815                   throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17816                     tpl, resp.status, resp.statusText);
17817                 }
17818                 return $q.reject(resp);
17819               }
17820             }
17821
17822             handleRequestFn.totalPendingRequests = 0;
17823
17824             return handleRequestFn;
17825           }];
17826         }
17827
17828         function $$TestabilityProvider() {
17829           this.$get = ['$rootScope', '$browser', '$location',
17830                function($rootScope,   $browser,   $location) {
17831
17832             /**
17833              * @name $testability
17834              *
17835              * @description
17836              * The private $$testability service provides a collection of methods for use when debugging
17837              * or by automated test and debugging tools.
17838              */
17839             var testability = {};
17840
17841             /**
17842              * @name $$testability#findBindings
17843              *
17844              * @description
17845              * Returns an array of elements that are bound (via ng-bind or {{}})
17846              * to expressions matching the input.
17847              *
17848              * @param {Element} element The element root to search from.
17849              * @param {string} expression The binding expression to match.
17850              * @param {boolean} opt_exactMatch If true, only returns exact matches
17851              *     for the expression. Filters and whitespace are ignored.
17852              */
17853             testability.findBindings = function(element, expression, opt_exactMatch) {
17854               var bindings = element.getElementsByClassName('ng-binding');
17855               var matches = [];
17856               forEach(bindings, function(binding) {
17857                 var dataBinding = angular.element(binding).data('$binding');
17858                 if (dataBinding) {
17859                   forEach(dataBinding, function(bindingName) {
17860                     if (opt_exactMatch) {
17861                       var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17862                       if (matcher.test(bindingName)) {
17863                         matches.push(binding);
17864                       }
17865                     } else {
17866                       if (bindingName.indexOf(expression) != -1) {
17867                         matches.push(binding);
17868                       }
17869                     }
17870                   });
17871                 }
17872               });
17873               return matches;
17874             };
17875
17876             /**
17877              * @name $$testability#findModels
17878              *
17879              * @description
17880              * Returns an array of elements that are two-way found via ng-model to
17881              * expressions matching the input.
17882              *
17883              * @param {Element} element The element root to search from.
17884              * @param {string} expression The model expression to match.
17885              * @param {boolean} opt_exactMatch If true, only returns exact matches
17886              *     for the expression.
17887              */
17888             testability.findModels = function(element, expression, opt_exactMatch) {
17889               var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17890               for (var p = 0; p < prefixes.length; ++p) {
17891                 var attributeEquals = opt_exactMatch ? '=' : '*=';
17892                 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17893                 var elements = element.querySelectorAll(selector);
17894                 if (elements.length) {
17895                   return elements;
17896                 }
17897               }
17898             };
17899
17900             /**
17901              * @name $$testability#getLocation
17902              *
17903              * @description
17904              * Shortcut for getting the location in a browser agnostic way. Returns
17905              *     the path, search, and hash. (e.g. /path?a=b#hash)
17906              */
17907             testability.getLocation = function() {
17908               return $location.url();
17909             };
17910
17911             /**
17912              * @name $$testability#setLocation
17913              *
17914              * @description
17915              * Shortcut for navigating to a location without doing a full page reload.
17916              *
17917              * @param {string} url The location url (path, search and hash,
17918              *     e.g. /path?a=b#hash) to go to.
17919              */
17920             testability.setLocation = function(url) {
17921               if (url !== $location.url()) {
17922                 $location.url(url);
17923                 $rootScope.$digest();
17924               }
17925             };
17926
17927             /**
17928              * @name $$testability#whenStable
17929              *
17930              * @description
17931              * Calls the callback when $timeout and $http requests are completed.
17932              *
17933              * @param {function} callback
17934              */
17935             testability.whenStable = function(callback) {
17936               $browser.notifyWhenNoOutstandingRequests(callback);
17937             };
17938
17939             return testability;
17940           }];
17941         }
17942
17943         function $TimeoutProvider() {
17944           this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17945                function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17946
17947             var deferreds = {};
17948
17949
17950              /**
17951               * @ngdoc service
17952               * @name $timeout
17953               *
17954               * @description
17955               * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17956               * block and delegates any exceptions to
17957               * {@link ng.$exceptionHandler $exceptionHandler} service.
17958               *
17959               * The return value of calling `$timeout` is a promise, which will be resolved when
17960               * the delay has passed and the timeout function, if provided, is executed.
17961               *
17962               * To cancel a timeout request, call `$timeout.cancel(promise)`.
17963               *
17964               * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17965               * synchronously flush the queue of deferred functions.
17966               *
17967               * If you only want a promise that will be resolved after some specified delay
17968               * then you can call `$timeout` without the `fn` function.
17969               *
17970               * @param {function()=} fn A function, whose execution should be delayed.
17971               * @param {number=} [delay=0] Delay in milliseconds.
17972               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17973               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17974               * @param {...*=} Pass additional parameters to the executed function.
17975               * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17976               *   promise will be resolved with is the return value of the `fn` function.
17977               *
17978               */
17979             function timeout(fn, delay, invokeApply) {
17980               if (!isFunction(fn)) {
17981                 invokeApply = delay;
17982                 delay = fn;
17983                 fn = noop;
17984               }
17985
17986               var args = sliceArgs(arguments, 3),
17987                   skipApply = (isDefined(invokeApply) && !invokeApply),
17988                   deferred = (skipApply ? $$q : $q).defer(),
17989                   promise = deferred.promise,
17990                   timeoutId;
17991
17992               timeoutId = $browser.defer(function() {
17993                 try {
17994                   deferred.resolve(fn.apply(null, args));
17995                 } catch (e) {
17996                   deferred.reject(e);
17997                   $exceptionHandler(e);
17998                 }
17999                 finally {
18000                   delete deferreds[promise.$$timeoutId];
18001                 }
18002
18003                 if (!skipApply) $rootScope.$apply();
18004               }, delay);
18005
18006               promise.$$timeoutId = timeoutId;
18007               deferreds[timeoutId] = deferred;
18008
18009               return promise;
18010             }
18011
18012
18013              /**
18014               * @ngdoc method
18015               * @name $timeout#cancel
18016               *
18017               * @description
18018               * Cancels a task associated with the `promise`. As a result of this, the promise will be
18019               * resolved with a rejection.
18020               *
18021               * @param {Promise=} promise Promise returned by the `$timeout` function.
18022               * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18023               *   canceled.
18024               */
18025             timeout.cancel = function(promise) {
18026               if (promise && promise.$$timeoutId in deferreds) {
18027                 deferreds[promise.$$timeoutId].reject('canceled');
18028                 delete deferreds[promise.$$timeoutId];
18029                 return $browser.defer.cancel(promise.$$timeoutId);
18030               }
18031               return false;
18032             };
18033
18034             return timeout;
18035           }];
18036         }
18037
18038         // NOTE:  The usage of window and document instead of $window and $document here is
18039         // deliberate.  This service depends on the specific behavior of anchor nodes created by the
18040         // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18041         // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18042         // doesn't know about mocked locations and resolves URLs to the real document - which is
18043         // exactly the behavior needed here.  There is little value is mocking these out for this
18044         // service.
18045         var urlParsingNode = document.createElement("a");
18046         var originUrl = urlResolve(window.location.href);
18047
18048
18049         /**
18050          *
18051          * Implementation Notes for non-IE browsers
18052          * ----------------------------------------
18053          * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18054          * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18055          * URL will be resolved into an absolute URL in the context of the application document.
18056          * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18057          * properties are all populated to reflect the normalized URL.  This approach has wide
18058          * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18059          * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18060          *
18061          * Implementation Notes for IE
18062          * ---------------------------
18063          * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18064          * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18065          * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18066          * work around that by performing the parsing in a 2nd step by taking a previously normalized
18067          * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18068          * properties such as protocol, hostname, port, etc.
18069          *
18070          * References:
18071          *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18072          *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18073          *   http://url.spec.whatwg.org/#urlutils
18074          *   https://github.com/angular/angular.js/pull/2902
18075          *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18076          *
18077          * @kind function
18078          * @param {string} url The URL to be parsed.
18079          * @description Normalizes and parses a URL.
18080          * @returns {object} Returns the normalized URL as a dictionary.
18081          *
18082          *   | member name   | Description    |
18083          *   |---------------|----------------|
18084          *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18085          *   | protocol      | The protocol including the trailing colon                              |
18086          *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18087          *   | search        | The search params, minus the question mark                             |
18088          *   | hash          | The hash string, minus the hash symbol
18089          *   | hostname      | The hostname
18090          *   | port          | The port, without ":"
18091          *   | pathname      | The pathname, beginning with "/"
18092          *
18093          */
18094         function urlResolve(url) {
18095           var href = url;
18096
18097           if (msie) {
18098             // Normalize before parse.  Refer Implementation Notes on why this is
18099             // done in two steps on IE.
18100             urlParsingNode.setAttribute("href", href);
18101             href = urlParsingNode.href;
18102           }
18103
18104           urlParsingNode.setAttribute('href', href);
18105
18106           // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18107           return {
18108             href: urlParsingNode.href,
18109             protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18110             host: urlParsingNode.host,
18111             search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18112             hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18113             hostname: urlParsingNode.hostname,
18114             port: urlParsingNode.port,
18115             pathname: (urlParsingNode.pathname.charAt(0) === '/')
18116               ? urlParsingNode.pathname
18117               : '/' + urlParsingNode.pathname
18118           };
18119         }
18120
18121         /**
18122          * Parse a request URL and determine whether this is a same-origin request as the application document.
18123          *
18124          * @param {string|object} requestUrl The url of the request as a string that will be resolved
18125          * or a parsed URL object.
18126          * @returns {boolean} Whether the request is for the same origin as the application document.
18127          */
18128         function urlIsSameOrigin(requestUrl) {
18129           var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18130           return (parsed.protocol === originUrl.protocol &&
18131                   parsed.host === originUrl.host);
18132         }
18133
18134         /**
18135          * @ngdoc service
18136          * @name $window
18137          *
18138          * @description
18139          * A reference to the browser's `window` object. While `window`
18140          * is globally available in JavaScript, it causes testability problems, because
18141          * it is a global variable. In angular we always refer to it through the
18142          * `$window` service, so it may be overridden, removed or mocked for testing.
18143          *
18144          * Expressions, like the one defined for the `ngClick` directive in the example
18145          * below, are evaluated with respect to the current scope.  Therefore, there is
18146          * no risk of inadvertently coding in a dependency on a global value in such an
18147          * expression.
18148          *
18149          * @example
18150            <example module="windowExample">
18151              <file name="index.html">
18152                <script>
18153                  angular.module('windowExample', [])
18154                    .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18155                      $scope.greeting = 'Hello, World!';
18156                      $scope.doGreeting = function(greeting) {
18157                        $window.alert(greeting);
18158                      };
18159                    }]);
18160                </script>
18161                <div ng-controller="ExampleController">
18162                  <input type="text" ng-model="greeting" aria-label="greeting" />
18163                  <button ng-click="doGreeting(greeting)">ALERT</button>
18164                </div>
18165              </file>
18166              <file name="protractor.js" type="protractor">
18167               it('should display the greeting in the input box', function() {
18168                element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18169                // If we click the button it will block the test runner
18170                // element(':button').click();
18171               });
18172              </file>
18173            </example>
18174          */
18175         function $WindowProvider() {
18176           this.$get = valueFn(window);
18177         }
18178
18179         /**
18180          * @name $$cookieReader
18181          * @requires $document
18182          *
18183          * @description
18184          * This is a private service for reading cookies used by $http and ngCookies
18185          *
18186          * @return {Object} a key/value map of the current cookies
18187          */
18188         function $$CookieReader($document) {
18189           var rawDocument = $document[0] || {};
18190           var lastCookies = {};
18191           var lastCookieString = '';
18192
18193           function safeDecodeURIComponent(str) {
18194             try {
18195               return decodeURIComponent(str);
18196             } catch (e) {
18197               return str;
18198             }
18199           }
18200
18201           return function() {
18202             var cookieArray, cookie, i, index, name;
18203             var currentCookieString = rawDocument.cookie || '';
18204
18205             if (currentCookieString !== lastCookieString) {
18206               lastCookieString = currentCookieString;
18207               cookieArray = lastCookieString.split('; ');
18208               lastCookies = {};
18209
18210               for (i = 0; i < cookieArray.length; i++) {
18211                 cookie = cookieArray[i];
18212                 index = cookie.indexOf('=');
18213                 if (index > 0) { //ignore nameless cookies
18214                   name = safeDecodeURIComponent(cookie.substring(0, index));
18215                   // the first value that is seen for a cookie is the most
18216                   // specific one.  values for the same cookie name that
18217                   // follow are for less specific paths.
18218                   if (isUndefined(lastCookies[name])) {
18219                     lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18220                   }
18221                 }
18222               }
18223             }
18224             return lastCookies;
18225           };
18226         }
18227
18228         $$CookieReader.$inject = ['$document'];
18229
18230         function $$CookieReaderProvider() {
18231           this.$get = $$CookieReader;
18232         }
18233
18234         /* global currencyFilter: true,
18235          dateFilter: true,
18236          filterFilter: true,
18237          jsonFilter: true,
18238          limitToFilter: true,
18239          lowercaseFilter: true,
18240          numberFilter: true,
18241          orderByFilter: true,
18242          uppercaseFilter: true,
18243          */
18244
18245         /**
18246          * @ngdoc provider
18247          * @name $filterProvider
18248          * @description
18249          *
18250          * Filters are just functions which transform input to an output. However filters need to be
18251          * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18252          * annotated with dependencies and is responsible for creating a filter function.
18253          *
18254          * <div class="alert alert-warning">
18255          * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18256          * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18257          * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18258          * (`myapp_subsection_filterx`).
18259          * </div>
18260          *
18261          * ```js
18262          *   // Filter registration
18263          *   function MyModule($provide, $filterProvider) {
18264          *     // create a service to demonstrate injection (not always needed)
18265          *     $provide.value('greet', function(name){
18266          *       return 'Hello ' + name + '!';
18267          *     });
18268          *
18269          *     // register a filter factory which uses the
18270          *     // greet service to demonstrate DI.
18271          *     $filterProvider.register('greet', function(greet){
18272          *       // return the filter function which uses the greet service
18273          *       // to generate salutation
18274          *       return function(text) {
18275          *         // filters need to be forgiving so check input validity
18276          *         return text && greet(text) || text;
18277          *       };
18278          *     });
18279          *   }
18280          * ```
18281          *
18282          * The filter function is registered with the `$injector` under the filter name suffix with
18283          * `Filter`.
18284          *
18285          * ```js
18286          *   it('should be the same instance', inject(
18287          *     function($filterProvider) {
18288          *       $filterProvider.register('reverse', function(){
18289          *         return ...;
18290          *       });
18291          *     },
18292          *     function($filter, reverseFilter) {
18293          *       expect($filter('reverse')).toBe(reverseFilter);
18294          *     });
18295          * ```
18296          *
18297          *
18298          * For more information about how angular filters work, and how to create your own filters, see
18299          * {@link guide/filter Filters} in the Angular Developer Guide.
18300          */
18301
18302         /**
18303          * @ngdoc service
18304          * @name $filter
18305          * @kind function
18306          * @description
18307          * Filters are used for formatting data displayed to the user.
18308          *
18309          * The general syntax in templates is as follows:
18310          *
18311          *         {{ expression [| filter_name[:parameter_value] ... ] }}
18312          *
18313          * @param {String} name Name of the filter function to retrieve
18314          * @return {Function} the filter function
18315          * @example
18316            <example name="$filter" module="filterExample">
18317              <file name="index.html">
18318                <div ng-controller="MainCtrl">
18319                 <h3>{{ originalText }}</h3>
18320                 <h3>{{ filteredText }}</h3>
18321                </div>
18322              </file>
18323
18324              <file name="script.js">
18325               angular.module('filterExample', [])
18326               .controller('MainCtrl', function($scope, $filter) {
18327                 $scope.originalText = 'hello';
18328                 $scope.filteredText = $filter('uppercase')($scope.originalText);
18329               });
18330              </file>
18331            </example>
18332           */
18333         $FilterProvider.$inject = ['$provide'];
18334         function $FilterProvider($provide) {
18335           var suffix = 'Filter';
18336
18337           /**
18338            * @ngdoc method
18339            * @name $filterProvider#register
18340            * @param {string|Object} name Name of the filter function, or an object map of filters where
18341            *    the keys are the filter names and the values are the filter factories.
18342            *
18343            *    <div class="alert alert-warning">
18344            *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18345            *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18346            *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18347            *    (`myapp_subsection_filterx`).
18348            *    </div>
18349             * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18350            * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18351            *    of the registered filter instances.
18352            */
18353           function register(name, factory) {
18354             if (isObject(name)) {
18355               var filters = {};
18356               forEach(name, function(filter, key) {
18357                 filters[key] = register(key, filter);
18358               });
18359               return filters;
18360             } else {
18361               return $provide.factory(name + suffix, factory);
18362             }
18363           }
18364           this.register = register;
18365
18366           this.$get = ['$injector', function($injector) {
18367             return function(name) {
18368               return $injector.get(name + suffix);
18369             };
18370           }];
18371
18372           ////////////////////////////////////////
18373
18374           /* global
18375             currencyFilter: false,
18376             dateFilter: false,
18377             filterFilter: false,
18378             jsonFilter: false,
18379             limitToFilter: false,
18380             lowercaseFilter: false,
18381             numberFilter: false,
18382             orderByFilter: false,
18383             uppercaseFilter: false,
18384           */
18385
18386           register('currency', currencyFilter);
18387           register('date', dateFilter);
18388           register('filter', filterFilter);
18389           register('json', jsonFilter);
18390           register('limitTo', limitToFilter);
18391           register('lowercase', lowercaseFilter);
18392           register('number', numberFilter);
18393           register('orderBy', orderByFilter);
18394           register('uppercase', uppercaseFilter);
18395         }
18396
18397         /**
18398          * @ngdoc filter
18399          * @name filter
18400          * @kind function
18401          *
18402          * @description
18403          * Selects a subset of items from `array` and returns it as a new array.
18404          *
18405          * @param {Array} array The source array.
18406          * @param {string|Object|function()} expression The predicate to be used for selecting items from
18407          *   `array`.
18408          *
18409          *   Can be one of:
18410          *
18411          *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18412          *     objects with string properties in `array` that match this string will be returned. This also
18413          *     applies to nested object properties.
18414          *     The predicate can be negated by prefixing the string with `!`.
18415          *
18416          *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18417          *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18418          *     which have property `name` containing "M" and property `phone` containing "1". A special
18419          *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18420          *     property of the object or its nested object properties. That's equivalent to the simple
18421          *     substring match with a `string` as described above. The predicate can be negated by prefixing
18422          *     the string with `!`.
18423          *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18424          *     not containing "M".
18425          *
18426          *     Note that a named property will match properties on the same level only, while the special
18427          *     `$` property will match properties on the same level or deeper. E.g. an array item like
18428          *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18429          *     **will** be matched by `{$: 'John'}`.
18430          *
18431          *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18432          *     The function is called for each element of the array, with the element, its index, and
18433          *     the entire array itself as arguments.
18434          *
18435          *     The final result is an array of those elements that the predicate returned true for.
18436          *
18437          * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18438          *     determining if the expected value (from the filter expression) and actual value (from
18439          *     the object in the array) should be considered a match.
18440          *
18441          *   Can be one of:
18442          *
18443          *   - `function(actual, expected)`:
18444          *     The function will be given the object value and the predicate value to compare and
18445          *     should return true if both values should be considered equal.
18446          *
18447          *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18448          *     This is essentially strict comparison of expected and actual.
18449          *
18450          *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18451          *     insensitive way.
18452          *
18453          *     Primitive values are converted to strings. Objects are not compared against primitives,
18454          *     unless they have a custom `toString` method (e.g. `Date` objects).
18455          *
18456          * @example
18457            <example>
18458              <file name="index.html">
18459                <div ng-init="friends = [{name:'John', phone:'555-1276'},
18460                                         {name:'Mary', phone:'800-BIG-MARY'},
18461                                         {name:'Mike', phone:'555-4321'},
18462                                         {name:'Adam', phone:'555-5678'},
18463                                         {name:'Julie', phone:'555-8765'},
18464                                         {name:'Juliette', phone:'555-5678'}]"></div>
18465
18466                <label>Search: <input ng-model="searchText"></label>
18467                <table id="searchTextResults">
18468                  <tr><th>Name</th><th>Phone</th></tr>
18469                  <tr ng-repeat="friend in friends | filter:searchText">
18470                    <td>{{friend.name}}</td>
18471                    <td>{{friend.phone}}</td>
18472                  </tr>
18473                </table>
18474                <hr>
18475                <label>Any: <input ng-model="search.$"></label> <br>
18476                <label>Name only <input ng-model="search.name"></label><br>
18477                <label>Phone only <input ng-model="search.phone"></label><br>
18478                <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18479                <table id="searchObjResults">
18480                  <tr><th>Name</th><th>Phone</th></tr>
18481                  <tr ng-repeat="friendObj in friends | filter:search:strict">
18482                    <td>{{friendObj.name}}</td>
18483                    <td>{{friendObj.phone}}</td>
18484                  </tr>
18485                </table>
18486              </file>
18487              <file name="protractor.js" type="protractor">
18488                var expectFriendNames = function(expectedNames, key) {
18489                  element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18490                    arr.forEach(function(wd, i) {
18491                      expect(wd.getText()).toMatch(expectedNames[i]);
18492                    });
18493                  });
18494                };
18495
18496                it('should search across all fields when filtering with a string', function() {
18497                  var searchText = element(by.model('searchText'));
18498                  searchText.clear();
18499                  searchText.sendKeys('m');
18500                  expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18501
18502                  searchText.clear();
18503                  searchText.sendKeys('76');
18504                  expectFriendNames(['John', 'Julie'], 'friend');
18505                });
18506
18507                it('should search in specific fields when filtering with a predicate object', function() {
18508                  var searchAny = element(by.model('search.$'));
18509                  searchAny.clear();
18510                  searchAny.sendKeys('i');
18511                  expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18512                });
18513                it('should use a equal comparison when comparator is true', function() {
18514                  var searchName = element(by.model('search.name'));
18515                  var strict = element(by.model('strict'));
18516                  searchName.clear();
18517                  searchName.sendKeys('Julie');
18518                  strict.click();
18519                  expectFriendNames(['Julie'], 'friendObj');
18520                });
18521              </file>
18522            </example>
18523          */
18524         function filterFilter() {
18525           return function(array, expression, comparator) {
18526             if (!isArrayLike(array)) {
18527               if (array == null) {
18528                 return array;
18529               } else {
18530                 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18531               }
18532             }
18533
18534             var expressionType = getTypeForFilter(expression);
18535             var predicateFn;
18536             var matchAgainstAnyProp;
18537
18538             switch (expressionType) {
18539               case 'function':
18540                 predicateFn = expression;
18541                 break;
18542               case 'boolean':
18543               case 'null':
18544               case 'number':
18545               case 'string':
18546                 matchAgainstAnyProp = true;
18547                 //jshint -W086
18548               case 'object':
18549                 //jshint +W086
18550                 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18551                 break;
18552               default:
18553                 return array;
18554             }
18555
18556             return Array.prototype.filter.call(array, predicateFn);
18557           };
18558         }
18559
18560         // Helper functions for `filterFilter`
18561         function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18562           var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18563           var predicateFn;
18564
18565           if (comparator === true) {
18566             comparator = equals;
18567           } else if (!isFunction(comparator)) {
18568             comparator = function(actual, expected) {
18569               if (isUndefined(actual)) {
18570                 // No substring matching against `undefined`
18571                 return false;
18572               }
18573               if ((actual === null) || (expected === null)) {
18574                 // No substring matching against `null`; only match against `null`
18575                 return actual === expected;
18576               }
18577               if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18578                 // Should not compare primitives against objects, unless they have custom `toString` method
18579                 return false;
18580               }
18581
18582               actual = lowercase('' + actual);
18583               expected = lowercase('' + expected);
18584               return actual.indexOf(expected) !== -1;
18585             };
18586           }
18587
18588           predicateFn = function(item) {
18589             if (shouldMatchPrimitives && !isObject(item)) {
18590               return deepCompare(item, expression.$, comparator, false);
18591             }
18592             return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18593           };
18594
18595           return predicateFn;
18596         }
18597
18598         function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18599           var actualType = getTypeForFilter(actual);
18600           var expectedType = getTypeForFilter(expected);
18601
18602           if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18603             return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18604           } else if (isArray(actual)) {
18605             // In case `actual` is an array, consider it a match
18606             // if ANY of it's items matches `expected`
18607             return actual.some(function(item) {
18608               return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18609             });
18610           }
18611
18612           switch (actualType) {
18613             case 'object':
18614               var key;
18615               if (matchAgainstAnyProp) {
18616                 for (key in actual) {
18617                   if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18618                     return true;
18619                   }
18620                 }
18621                 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18622               } else if (expectedType === 'object') {
18623                 for (key in expected) {
18624                   var expectedVal = expected[key];
18625                   if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18626                     continue;
18627                   }
18628
18629                   var matchAnyProperty = key === '$';
18630                   var actualVal = matchAnyProperty ? actual : actual[key];
18631                   if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18632                     return false;
18633                   }
18634                 }
18635                 return true;
18636               } else {
18637                 return comparator(actual, expected);
18638               }
18639               break;
18640             case 'function':
18641               return false;
18642             default:
18643               return comparator(actual, expected);
18644           }
18645         }
18646
18647         // Used for easily differentiating between `null` and actual `object`
18648         function getTypeForFilter(val) {
18649           return (val === null) ? 'null' : typeof val;
18650         }
18651
18652         /**
18653          * @ngdoc filter
18654          * @name currency
18655          * @kind function
18656          *
18657          * @description
18658          * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18659          * symbol for current locale is used.
18660          *
18661          * @param {number} amount Input to filter.
18662          * @param {string=} symbol Currency symbol or identifier to be displayed.
18663          * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18664          * @returns {string} Formatted number.
18665          *
18666          *
18667          * @example
18668            <example module="currencyExample">
18669              <file name="index.html">
18670                <script>
18671                  angular.module('currencyExample', [])
18672                    .controller('ExampleController', ['$scope', function($scope) {
18673                      $scope.amount = 1234.56;
18674                    }]);
18675                </script>
18676                <div ng-controller="ExampleController">
18677                  <input type="number" ng-model="amount" aria-label="amount"> <br>
18678                  default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18679                  custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18680                  no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18681                </div>
18682              </file>
18683              <file name="protractor.js" type="protractor">
18684                it('should init with 1234.56', function() {
18685                  expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18686                  expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18687                  expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18688                });
18689                it('should update', function() {
18690                  if (browser.params.browser == 'safari') {
18691                    // Safari does not understand the minus key. See
18692                    // https://github.com/angular/protractor/issues/481
18693                    return;
18694                  }
18695                  element(by.model('amount')).clear();
18696                  element(by.model('amount')).sendKeys('-1234');
18697                  expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18698                  expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18699                  expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18700                });
18701              </file>
18702            </example>
18703          */
18704         currencyFilter.$inject = ['$locale'];
18705         function currencyFilter($locale) {
18706           var formats = $locale.NUMBER_FORMATS;
18707           return function(amount, currencySymbol, fractionSize) {
18708             if (isUndefined(currencySymbol)) {
18709               currencySymbol = formats.CURRENCY_SYM;
18710             }
18711
18712             if (isUndefined(fractionSize)) {
18713               fractionSize = formats.PATTERNS[1].maxFrac;
18714             }
18715
18716             // if null or undefined pass it through
18717             return (amount == null)
18718                 ? amount
18719                 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18720                     replace(/\u00A4/g, currencySymbol);
18721           };
18722         }
18723
18724         /**
18725          * @ngdoc filter
18726          * @name number
18727          * @kind function
18728          *
18729          * @description
18730          * Formats a number as text.
18731          *
18732          * If the input is null or undefined, it will just be returned.
18733          * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18734          * If the input is not a number an empty string is returned.
18735          *
18736          *
18737          * @param {number|string} number Number to format.
18738          * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18739          * If this is not provided then the fraction size is computed from the current locale's number
18740          * formatting pattern. In the case of the default locale, it will be 3.
18741          * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18742          *
18743          * @example
18744            <example module="numberFilterExample">
18745              <file name="index.html">
18746                <script>
18747                  angular.module('numberFilterExample', [])
18748                    .controller('ExampleController', ['$scope', function($scope) {
18749                      $scope.val = 1234.56789;
18750                    }]);
18751                </script>
18752                <div ng-controller="ExampleController">
18753                  <label>Enter number: <input ng-model='val'></label><br>
18754                  Default formatting: <span id='number-default'>{{val | number}}</span><br>
18755                  No fractions: <span>{{val | number:0}}</span><br>
18756                  Negative number: <span>{{-val | number:4}}</span>
18757                </div>
18758              </file>
18759              <file name="protractor.js" type="protractor">
18760                it('should format numbers', function() {
18761                  expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18762                  expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18763                  expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18764                });
18765
18766                it('should update', function() {
18767                  element(by.model('val')).clear();
18768                  element(by.model('val')).sendKeys('3374.333');
18769                  expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18770                  expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18771                  expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18772               });
18773              </file>
18774            </example>
18775          */
18776
18777
18778         numberFilter.$inject = ['$locale'];
18779         function numberFilter($locale) {
18780           var formats = $locale.NUMBER_FORMATS;
18781           return function(number, fractionSize) {
18782
18783             // if null or undefined pass it through
18784             return (number == null)
18785                 ? number
18786                 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18787                                fractionSize);
18788           };
18789         }
18790
18791         var DECIMAL_SEP = '.';
18792         function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18793           if (isObject(number)) return '';
18794
18795           var isNegative = number < 0;
18796           number = Math.abs(number);
18797
18798           var isInfinity = number === Infinity;
18799           if (!isInfinity && !isFinite(number)) return '';
18800
18801           var numStr = number + '',
18802               formatedText = '',
18803               hasExponent = false,
18804               parts = [];
18805
18806           if (isInfinity) formatedText = '\u221e';
18807
18808           if (!isInfinity && numStr.indexOf('e') !== -1) {
18809             var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18810             if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18811               number = 0;
18812             } else {
18813               formatedText = numStr;
18814               hasExponent = true;
18815             }
18816           }
18817
18818           if (!isInfinity && !hasExponent) {
18819             var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18820
18821             // determine fractionSize if it is not specified
18822             if (isUndefined(fractionSize)) {
18823               fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18824             }
18825
18826             // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18827             // inspired by:
18828             // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18829             number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18830
18831             var fraction = ('' + number).split(DECIMAL_SEP);
18832             var whole = fraction[0];
18833             fraction = fraction[1] || '';
18834
18835             var i, pos = 0,
18836                 lgroup = pattern.lgSize,
18837                 group = pattern.gSize;
18838
18839             if (whole.length >= (lgroup + group)) {
18840               pos = whole.length - lgroup;
18841               for (i = 0; i < pos; i++) {
18842                 if ((pos - i) % group === 0 && i !== 0) {
18843                   formatedText += groupSep;
18844                 }
18845                 formatedText += whole.charAt(i);
18846               }
18847             }
18848
18849             for (i = pos; i < whole.length; i++) {
18850               if ((whole.length - i) % lgroup === 0 && i !== 0) {
18851                 formatedText += groupSep;
18852               }
18853               formatedText += whole.charAt(i);
18854             }
18855
18856             // format fraction part.
18857             while (fraction.length < fractionSize) {
18858               fraction += '0';
18859             }
18860
18861             if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18862           } else {
18863             if (fractionSize > 0 && number < 1) {
18864               formatedText = number.toFixed(fractionSize);
18865               number = parseFloat(formatedText);
18866               formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18867             }
18868           }
18869
18870           if (number === 0) {
18871             isNegative = false;
18872           }
18873
18874           parts.push(isNegative ? pattern.negPre : pattern.posPre,
18875                      formatedText,
18876                      isNegative ? pattern.negSuf : pattern.posSuf);
18877           return parts.join('');
18878         }
18879
18880         function padNumber(num, digits, trim) {
18881           var neg = '';
18882           if (num < 0) {
18883             neg =  '-';
18884             num = -num;
18885           }
18886           num = '' + num;
18887           while (num.length < digits) num = '0' + num;
18888           if (trim) {
18889             num = num.substr(num.length - digits);
18890           }
18891           return neg + num;
18892         }
18893
18894
18895         function dateGetter(name, size, offset, trim) {
18896           offset = offset || 0;
18897           return function(date) {
18898             var value = date['get' + name]();
18899             if (offset > 0 || value > -offset) {
18900               value += offset;
18901             }
18902             if (value === 0 && offset == -12) value = 12;
18903             return padNumber(value, size, trim);
18904           };
18905         }
18906
18907         function dateStrGetter(name, shortForm) {
18908           return function(date, formats) {
18909             var value = date['get' + name]();
18910             var get = uppercase(shortForm ? ('SHORT' + name) : name);
18911
18912             return formats[get][value];
18913           };
18914         }
18915
18916         function timeZoneGetter(date, formats, offset) {
18917           var zone = -1 * offset;
18918           var paddedZone = (zone >= 0) ? "+" : "";
18919
18920           paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18921                         padNumber(Math.abs(zone % 60), 2);
18922
18923           return paddedZone;
18924         }
18925
18926         function getFirstThursdayOfYear(year) {
18927             // 0 = index of January
18928             var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18929             // 4 = index of Thursday (+1 to account for 1st = 5)
18930             // 11 = index of *next* Thursday (+1 account for 1st = 12)
18931             return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18932         }
18933
18934         function getThursdayThisWeek(datetime) {
18935             return new Date(datetime.getFullYear(), datetime.getMonth(),
18936               // 4 = index of Thursday
18937               datetime.getDate() + (4 - datetime.getDay()));
18938         }
18939
18940         function weekGetter(size) {
18941            return function(date) {
18942               var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18943                  thisThurs = getThursdayThisWeek(date);
18944
18945               var diff = +thisThurs - +firstThurs,
18946                  result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18947
18948               return padNumber(result, size);
18949            };
18950         }
18951
18952         function ampmGetter(date, formats) {
18953           return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18954         }
18955
18956         function eraGetter(date, formats) {
18957           return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18958         }
18959
18960         function longEraGetter(date, formats) {
18961           return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18962         }
18963
18964         var DATE_FORMATS = {
18965           yyyy: dateGetter('FullYear', 4),
18966             yy: dateGetter('FullYear', 2, 0, true),
18967              y: dateGetter('FullYear', 1),
18968           MMMM: dateStrGetter('Month'),
18969            MMM: dateStrGetter('Month', true),
18970             MM: dateGetter('Month', 2, 1),
18971              M: dateGetter('Month', 1, 1),
18972             dd: dateGetter('Date', 2),
18973              d: dateGetter('Date', 1),
18974             HH: dateGetter('Hours', 2),
18975              H: dateGetter('Hours', 1),
18976             hh: dateGetter('Hours', 2, -12),
18977              h: dateGetter('Hours', 1, -12),
18978             mm: dateGetter('Minutes', 2),
18979              m: dateGetter('Minutes', 1),
18980             ss: dateGetter('Seconds', 2),
18981              s: dateGetter('Seconds', 1),
18982              // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18983              // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18984            sss: dateGetter('Milliseconds', 3),
18985           EEEE: dateStrGetter('Day'),
18986            EEE: dateStrGetter('Day', true),
18987              a: ampmGetter,
18988              Z: timeZoneGetter,
18989             ww: weekGetter(2),
18990              w: weekGetter(1),
18991              G: eraGetter,
18992              GG: eraGetter,
18993              GGG: eraGetter,
18994              GGGG: longEraGetter
18995         };
18996
18997         var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18998             NUMBER_STRING = /^\-?\d+$/;
18999
19000         /**
19001          * @ngdoc filter
19002          * @name date
19003          * @kind function
19004          *
19005          * @description
19006          *   Formats `date` to a string based on the requested `format`.
19007          *
19008          *   `format` string can be composed of the following elements:
19009          *
19010          *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19011          *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19012          *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19013          *   * `'MMMM'`: Month in year (January-December)
19014          *   * `'MMM'`: Month in year (Jan-Dec)
19015          *   * `'MM'`: Month in year, padded (01-12)
19016          *   * `'M'`: Month in year (1-12)
19017          *   * `'dd'`: Day in month, padded (01-31)
19018          *   * `'d'`: Day in month (1-31)
19019          *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19020          *   * `'EEE'`: Day in Week, (Sun-Sat)
19021          *   * `'HH'`: Hour in day, padded (00-23)
19022          *   * `'H'`: Hour in day (0-23)
19023          *   * `'hh'`: Hour in AM/PM, padded (01-12)
19024          *   * `'h'`: Hour in AM/PM, (1-12)
19025          *   * `'mm'`: Minute in hour, padded (00-59)
19026          *   * `'m'`: Minute in hour (0-59)
19027          *   * `'ss'`: Second in minute, padded (00-59)
19028          *   * `'s'`: Second in minute (0-59)
19029          *   * `'sss'`: Millisecond in second, padded (000-999)
19030          *   * `'a'`: AM/PM marker
19031          *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19032          *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19033          *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19034          *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19035          *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19036          *
19037          *   `format` string can also be one of the following predefined
19038          *   {@link guide/i18n localizable formats}:
19039          *
19040          *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19041          *     (e.g. Sep 3, 2010 12:05:08 PM)
19042          *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19043          *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19044          *     (e.g. Friday, September 3, 2010)
19045          *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19046          *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19047          *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19048          *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19049          *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19050          *
19051          *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19052          *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19053          *   (e.g. `"h 'o''clock'"`).
19054          *
19055          * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19056          *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19057          *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19058          *    specified in the string input, the time is considered to be in the local timezone.
19059          * @param {string=} format Formatting rules (see Description). If not specified,
19060          *    `mediumDate` is used.
19061          * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19062          *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19063          *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19064          *    If not specified, the timezone of the browser will be used.
19065          * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19066          *
19067          * @example
19068            <example>
19069              <file name="index.html">
19070                <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19071                    <span>{{1288323623006 | date:'medium'}}</span><br>
19072                <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19073                   <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19074                <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19075                   <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19076                <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19077                   <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19078              </file>
19079              <file name="protractor.js" type="protractor">
19080                it('should format date', function() {
19081                  expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19082                     toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19083                  expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19084                     toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19085                  expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19086                     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19087                  expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19088                     toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19089                });
19090              </file>
19091            </example>
19092          */
19093         dateFilter.$inject = ['$locale'];
19094         function dateFilter($locale) {
19095
19096
19097           var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19098                              // 1        2       3         4          5          6          7          8  9     10      11
19099           function jsonStringToDate(string) {
19100             var match;
19101             if (match = string.match(R_ISO8601_STR)) {
19102               var date = new Date(0),
19103                   tzHour = 0,
19104                   tzMin  = 0,
19105                   dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19106                   timeSetter = match[8] ? date.setUTCHours : date.setHours;
19107
19108               if (match[9]) {
19109                 tzHour = toInt(match[9] + match[10]);
19110                 tzMin = toInt(match[9] + match[11]);
19111               }
19112               dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19113               var h = toInt(match[4] || 0) - tzHour;
19114               var m = toInt(match[5] || 0) - tzMin;
19115               var s = toInt(match[6] || 0);
19116               var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19117               timeSetter.call(date, h, m, s, ms);
19118               return date;
19119             }
19120             return string;
19121           }
19122
19123
19124           return function(date, format, timezone) {
19125             var text = '',
19126                 parts = [],
19127                 fn, match;
19128
19129             format = format || 'mediumDate';
19130             format = $locale.DATETIME_FORMATS[format] || format;
19131             if (isString(date)) {
19132               date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19133             }
19134
19135             if (isNumber(date)) {
19136               date = new Date(date);
19137             }
19138
19139             if (!isDate(date) || !isFinite(date.getTime())) {
19140               return date;
19141             }
19142
19143             while (format) {
19144               match = DATE_FORMATS_SPLIT.exec(format);
19145               if (match) {
19146                 parts = concat(parts, match, 1);
19147                 format = parts.pop();
19148               } else {
19149                 parts.push(format);
19150                 format = null;
19151               }
19152             }
19153
19154             var dateTimezoneOffset = date.getTimezoneOffset();
19155             if (timezone) {
19156               dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19157               date = convertTimezoneToLocal(date, timezone, true);
19158             }
19159             forEach(parts, function(value) {
19160               fn = DATE_FORMATS[value];
19161               text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19162                          : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19163             });
19164
19165             return text;
19166           };
19167         }
19168
19169
19170         /**
19171          * @ngdoc filter
19172          * @name json
19173          * @kind function
19174          *
19175          * @description
19176          *   Allows you to convert a JavaScript object into JSON string.
19177          *
19178          *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19179          *   the binding is automatically converted to JSON.
19180          *
19181          * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19182          * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19183          * @returns {string} JSON string.
19184          *
19185          *
19186          * @example
19187            <example>
19188              <file name="index.html">
19189                <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19190                <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19191              </file>
19192              <file name="protractor.js" type="protractor">
19193                it('should jsonify filtered objects', function() {
19194                  expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19195                  expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19196                });
19197              </file>
19198            </example>
19199          *
19200          */
19201         function jsonFilter() {
19202           return function(object, spacing) {
19203             if (isUndefined(spacing)) {
19204                 spacing = 2;
19205             }
19206             return toJson(object, spacing);
19207           };
19208         }
19209
19210
19211         /**
19212          * @ngdoc filter
19213          * @name lowercase
19214          * @kind function
19215          * @description
19216          * Converts string to lowercase.
19217          * @see angular.lowercase
19218          */
19219         var lowercaseFilter = valueFn(lowercase);
19220
19221
19222         /**
19223          * @ngdoc filter
19224          * @name uppercase
19225          * @kind function
19226          * @description
19227          * Converts string to uppercase.
19228          * @see angular.uppercase
19229          */
19230         var uppercaseFilter = valueFn(uppercase);
19231
19232         /**
19233          * @ngdoc filter
19234          * @name limitTo
19235          * @kind function
19236          *
19237          * @description
19238          * Creates a new array or string containing only a specified number of elements. The elements
19239          * are taken from either the beginning or the end of the source array, string or number, as specified by
19240          * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19241          * converted to a string.
19242          *
19243          * @param {Array|string|number} input Source array, string or number to be limited.
19244          * @param {string|number} limit The length of the returned array or string. If the `limit` number
19245          *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19246          *     If the number is negative, `limit` number  of items from the end of the source array/string
19247          *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19248          *     the input will be returned unchanged.
19249          * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19250          *     indicates an offset from the end of `input`. Defaults to `0`.
19251          * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19252          *     had less than `limit` elements.
19253          *
19254          * @example
19255            <example module="limitToExample">
19256              <file name="index.html">
19257                <script>
19258                  angular.module('limitToExample', [])
19259                    .controller('ExampleController', ['$scope', function($scope) {
19260                      $scope.numbers = [1,2,3,4,5,6,7,8,9];
19261                      $scope.letters = "abcdefghi";
19262                      $scope.longNumber = 2345432342;
19263                      $scope.numLimit = 3;
19264                      $scope.letterLimit = 3;
19265                      $scope.longNumberLimit = 3;
19266                    }]);
19267                </script>
19268                <div ng-controller="ExampleController">
19269                  <label>
19270                     Limit {{numbers}} to:
19271                     <input type="number" step="1" ng-model="numLimit">
19272                  </label>
19273                  <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19274                  <label>
19275                     Limit {{letters}} to:
19276                     <input type="number" step="1" ng-model="letterLimit">
19277                  </label>
19278                  <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19279                  <label>
19280                     Limit {{longNumber}} to:
19281                     <input type="number" step="1" ng-model="longNumberLimit">
19282                  </label>
19283                  <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19284                </div>
19285              </file>
19286              <file name="protractor.js" type="protractor">
19287                var numLimitInput = element(by.model('numLimit'));
19288                var letterLimitInput = element(by.model('letterLimit'));
19289                var longNumberLimitInput = element(by.model('longNumberLimit'));
19290                var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19291                var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19292                var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19293
19294                it('should limit the number array to first three items', function() {
19295                  expect(numLimitInput.getAttribute('value')).toBe('3');
19296                  expect(letterLimitInput.getAttribute('value')).toBe('3');
19297                  expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19298                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19299                  expect(limitedLetters.getText()).toEqual('Output letters: abc');
19300                  expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19301                });
19302
19303                // There is a bug in safari and protractor that doesn't like the minus key
19304                // it('should update the output when -3 is entered', function() {
19305                //   numLimitInput.clear();
19306                //   numLimitInput.sendKeys('-3');
19307                //   letterLimitInput.clear();
19308                //   letterLimitInput.sendKeys('-3');
19309                //   longNumberLimitInput.clear();
19310                //   longNumberLimitInput.sendKeys('-3');
19311                //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19312                //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19313                //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19314                // });
19315
19316                it('should not exceed the maximum size of input array', function() {
19317                  numLimitInput.clear();
19318                  numLimitInput.sendKeys('100');
19319                  letterLimitInput.clear();
19320                  letterLimitInput.sendKeys('100');
19321                  longNumberLimitInput.clear();
19322                  longNumberLimitInput.sendKeys('100');
19323                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19324                  expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19325                  expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19326                });
19327              </file>
19328            </example>
19329         */
19330         function limitToFilter() {
19331           return function(input, limit, begin) {
19332             if (Math.abs(Number(limit)) === Infinity) {
19333               limit = Number(limit);
19334             } else {
19335               limit = toInt(limit);
19336             }
19337             if (isNaN(limit)) return input;
19338
19339             if (isNumber(input)) input = input.toString();
19340             if (!isArray(input) && !isString(input)) return input;
19341
19342             begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19343             begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19344
19345             if (limit >= 0) {
19346               return input.slice(begin, begin + limit);
19347             } else {
19348               if (begin === 0) {
19349                 return input.slice(limit, input.length);
19350               } else {
19351                 return input.slice(Math.max(0, begin + limit), begin);
19352               }
19353             }
19354           };
19355         }
19356
19357         /**
19358          * @ngdoc filter
19359          * @name orderBy
19360          * @kind function
19361          *
19362          * @description
19363          * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19364          * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19365          * as expected, make sure they are actually being saved as numbers and not strings.
19366          *
19367          * @param {Array} array The array to sort.
19368          * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19369          *    used by the comparator to determine the order of elements.
19370          *
19371          *    Can be one of:
19372          *
19373          *    - `function`: Getter function. The result of this function will be sorted using the
19374          *      `<`, `===`, `>` operator.
19375          *    - `string`: An Angular expression. The result of this expression is used to compare elements
19376          *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19377          *      3 first characters of a property called `name`). The result of a constant expression
19378          *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19379          *      to sort object by the value of their `special name` property). An expression can be
19380          *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19381          *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19382          *      element itself is used to compare where sorting.
19383          *    - `Array`: An array of function or string predicates. The first predicate in the array
19384          *      is used for sorting, but when two items are equivalent, the next predicate is used.
19385          *
19386          *    If the predicate is missing or empty then it defaults to `'+'`.
19387          *
19388          * @param {boolean=} reverse Reverse the order of the array.
19389          * @returns {Array} Sorted copy of the source array.
19390          *
19391          *
19392          * @example
19393          * The example below demonstrates a simple ngRepeat, where the data is sorted
19394          * by age in descending order (predicate is set to `'-age'`).
19395          * `reverse` is not set, which means it defaults to `false`.
19396            <example module="orderByExample">
19397              <file name="index.html">
19398                <script>
19399                  angular.module('orderByExample', [])
19400                    .controller('ExampleController', ['$scope', function($scope) {
19401                      $scope.friends =
19402                          [{name:'John', phone:'555-1212', age:10},
19403                           {name:'Mary', phone:'555-9876', age:19},
19404                           {name:'Mike', phone:'555-4321', age:21},
19405                           {name:'Adam', phone:'555-5678', age:35},
19406                           {name:'Julie', phone:'555-8765', age:29}];
19407                    }]);
19408                </script>
19409                <div ng-controller="ExampleController">
19410                  <table class="friend">
19411                    <tr>
19412                      <th>Name</th>
19413                      <th>Phone Number</th>
19414                      <th>Age</th>
19415                    </tr>
19416                    <tr ng-repeat="friend in friends | orderBy:'-age'">
19417                      <td>{{friend.name}}</td>
19418                      <td>{{friend.phone}}</td>
19419                      <td>{{friend.age}}</td>
19420                    </tr>
19421                  </table>
19422                </div>
19423              </file>
19424            </example>
19425          *
19426          * The predicate and reverse parameters can be controlled dynamically through scope properties,
19427          * as shown in the next example.
19428          * @example
19429            <example module="orderByExample">
19430              <file name="index.html">
19431                <script>
19432                  angular.module('orderByExample', [])
19433                    .controller('ExampleController', ['$scope', function($scope) {
19434                      $scope.friends =
19435                          [{name:'John', phone:'555-1212', age:10},
19436                           {name:'Mary', phone:'555-9876', age:19},
19437                           {name:'Mike', phone:'555-4321', age:21},
19438                           {name:'Adam', phone:'555-5678', age:35},
19439                           {name:'Julie', phone:'555-8765', age:29}];
19440                      $scope.predicate = 'age';
19441                      $scope.reverse = true;
19442                      $scope.order = function(predicate) {
19443                        $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19444                        $scope.predicate = predicate;
19445                      };
19446                    }]);
19447                </script>
19448                <style type="text/css">
19449                  .sortorder:after {
19450                    content: '\25b2';
19451                  }
19452                  .sortorder.reverse:after {
19453                    content: '\25bc';
19454                  }
19455                </style>
19456                <div ng-controller="ExampleController">
19457                  <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19458                  <hr/>
19459                  [ <a href="" ng-click="predicate=''">unsorted</a> ]
19460                  <table class="friend">
19461                    <tr>
19462                      <th>
19463                        <a href="" ng-click="order('name')">Name</a>
19464                        <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19465                      </th>
19466                      <th>
19467                        <a href="" ng-click="order('phone')">Phone Number</a>
19468                        <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19469                      </th>
19470                      <th>
19471                        <a href="" ng-click="order('age')">Age</a>
19472                        <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19473                      </th>
19474                    </tr>
19475                    <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19476                      <td>{{friend.name}}</td>
19477                      <td>{{friend.phone}}</td>
19478                      <td>{{friend.age}}</td>
19479                    </tr>
19480                  </table>
19481                </div>
19482              </file>
19483            </example>
19484          *
19485          * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19486          * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19487          * desired parameters.
19488          *
19489          * Example:
19490          *
19491          * @example
19492           <example module="orderByExample">
19493             <file name="index.html">
19494               <div ng-controller="ExampleController">
19495                 <table class="friend">
19496                   <tr>
19497                     <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19498                       (<a href="" ng-click="order('-name',false)">^</a>)</th>
19499                     <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19500                     <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19501                   </tr>
19502                   <tr ng-repeat="friend in friends">
19503                     <td>{{friend.name}}</td>
19504                     <td>{{friend.phone}}</td>
19505                     <td>{{friend.age}}</td>
19506                   </tr>
19507                 </table>
19508               </div>
19509             </file>
19510
19511             <file name="script.js">
19512               angular.module('orderByExample', [])
19513                 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19514                   var orderBy = $filter('orderBy');
19515                   $scope.friends = [
19516                     { name: 'John',    phone: '555-1212',    age: 10 },
19517                     { name: 'Mary',    phone: '555-9876',    age: 19 },
19518                     { name: 'Mike',    phone: '555-4321',    age: 21 },
19519                     { name: 'Adam',    phone: '555-5678',    age: 35 },
19520                     { name: 'Julie',   phone: '555-8765',    age: 29 }
19521                   ];
19522                   $scope.order = function(predicate, reverse) {
19523                     $scope.friends = orderBy($scope.friends, predicate, reverse);
19524                   };
19525                   $scope.order('-age',false);
19526                 }]);
19527             </file>
19528         </example>
19529          */
19530         orderByFilter.$inject = ['$parse'];
19531         function orderByFilter($parse) {
19532           return function(array, sortPredicate, reverseOrder) {
19533
19534             if (!(isArrayLike(array))) return array;
19535
19536             if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19537             if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19538
19539             var predicates = processPredicates(sortPredicate, reverseOrder);
19540             // Add a predicate at the end that evaluates to the element index. This makes the
19541             // sort stable as it works as a tie-breaker when all the input predicates cannot
19542             // distinguish between two elements.
19543             predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19544
19545             // The next three lines are a version of a Swartzian Transform idiom from Perl
19546             // (sometimes called the Decorate-Sort-Undecorate idiom)
19547             // See https://en.wikipedia.org/wiki/Schwartzian_transform
19548             var compareValues = Array.prototype.map.call(array, getComparisonObject);
19549             compareValues.sort(doComparison);
19550             array = compareValues.map(function(item) { return item.value; });
19551
19552             return array;
19553
19554             function getComparisonObject(value, index) {
19555               return {
19556                 value: value,
19557                 predicateValues: predicates.map(function(predicate) {
19558                   return getPredicateValue(predicate.get(value), index);
19559                 })
19560               };
19561             }
19562
19563             function doComparison(v1, v2) {
19564               var result = 0;
19565               for (var index=0, length = predicates.length; index < length; ++index) {
19566                 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19567                 if (result) break;
19568               }
19569               return result;
19570             }
19571           };
19572
19573           function processPredicates(sortPredicate, reverseOrder) {
19574             reverseOrder = reverseOrder ? -1 : 1;
19575             return sortPredicate.map(function(predicate) {
19576               var descending = 1, get = identity;
19577
19578               if (isFunction(predicate)) {
19579                 get = predicate;
19580               } else if (isString(predicate)) {
19581                 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19582                   descending = predicate.charAt(0) == '-' ? -1 : 1;
19583                   predicate = predicate.substring(1);
19584                 }
19585                 if (predicate !== '') {
19586                   get = $parse(predicate);
19587                   if (get.constant) {
19588                     var key = get();
19589                     get = function(value) { return value[key]; };
19590                   }
19591                 }
19592               }
19593               return { get: get, descending: descending * reverseOrder };
19594             });
19595           }
19596
19597           function isPrimitive(value) {
19598             switch (typeof value) {
19599               case 'number': /* falls through */
19600               case 'boolean': /* falls through */
19601               case 'string':
19602                 return true;
19603               default:
19604                 return false;
19605             }
19606           }
19607
19608           function objectValue(value, index) {
19609             // If `valueOf` is a valid function use that
19610             if (typeof value.valueOf === 'function') {
19611               value = value.valueOf();
19612               if (isPrimitive(value)) return value;
19613             }
19614             // If `toString` is a valid function and not the one from `Object.prototype` use that
19615             if (hasCustomToString(value)) {
19616               value = value.toString();
19617               if (isPrimitive(value)) return value;
19618             }
19619             // We have a basic object so we use the position of the object in the collection
19620             return index;
19621           }
19622
19623           function getPredicateValue(value, index) {
19624             var type = typeof value;
19625             if (value === null) {
19626               type = 'string';
19627               value = 'null';
19628             } else if (type === 'string') {
19629               value = value.toLowerCase();
19630             } else if (type === 'object') {
19631               value = objectValue(value, index);
19632             }
19633             return { value: value, type: type };
19634           }
19635
19636           function compare(v1, v2) {
19637             var result = 0;
19638             if (v1.type === v2.type) {
19639               if (v1.value !== v2.value) {
19640                 result = v1.value < v2.value ? -1 : 1;
19641               }
19642             } else {
19643               result = v1.type < v2.type ? -1 : 1;
19644             }
19645             return result;
19646           }
19647         }
19648
19649         function ngDirective(directive) {
19650           if (isFunction(directive)) {
19651             directive = {
19652               link: directive
19653             };
19654           }
19655           directive.restrict = directive.restrict || 'AC';
19656           return valueFn(directive);
19657         }
19658
19659         /**
19660          * @ngdoc directive
19661          * @name a
19662          * @restrict E
19663          *
19664          * @description
19665          * Modifies the default behavior of the html A tag so that the default action is prevented when
19666          * the href attribute is empty.
19667          *
19668          * This change permits the easy creation of action links with the `ngClick` directive
19669          * without changing the location or causing page reloads, e.g.:
19670          * `<a href="" ng-click="list.addItem()">Add Item</a>`
19671          */
19672         var htmlAnchorDirective = valueFn({
19673           restrict: 'E',
19674           compile: function(element, attr) {
19675             if (!attr.href && !attr.xlinkHref) {
19676               return function(scope, element) {
19677                 // If the linked element is not an anchor tag anymore, do nothing
19678                 if (element[0].nodeName.toLowerCase() !== 'a') return;
19679
19680                 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19681                 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19682                            'xlink:href' : 'href';
19683                 element.on('click', function(event) {
19684                   // if we have no href url, then don't navigate anywhere.
19685                   if (!element.attr(href)) {
19686                     event.preventDefault();
19687                   }
19688                 });
19689               };
19690             }
19691           }
19692         });
19693
19694         /**
19695          * @ngdoc directive
19696          * @name ngHref
19697          * @restrict A
19698          * @priority 99
19699          *
19700          * @description
19701          * Using Angular markup like `{{hash}}` in an href attribute will
19702          * make the link go to the wrong URL if the user clicks it before
19703          * Angular has a chance to replace the `{{hash}}` markup with its
19704          * value. Until Angular replaces the markup the link will be broken
19705          * and will most likely return a 404 error. The `ngHref` directive
19706          * solves this problem.
19707          *
19708          * The wrong way to write it:
19709          * ```html
19710          * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19711          * ```
19712          *
19713          * The correct way to write it:
19714          * ```html
19715          * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19716          * ```
19717          *
19718          * @element A
19719          * @param {template} ngHref any string which can contain `{{}}` markup.
19720          *
19721          * @example
19722          * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19723          * in links and their different behaviors:
19724             <example>
19725               <file name="index.html">
19726                 <input ng-model="value" /><br />
19727                 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19728                 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19729                 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19730                 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19731                 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19732                 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19733               </file>
19734               <file name="protractor.js" type="protractor">
19735                 it('should execute ng-click but not reload when href without value', function() {
19736                   element(by.id('link-1')).click();
19737                   expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19738                   expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19739                 });
19740
19741                 it('should execute ng-click but not reload when href empty string', function() {
19742                   element(by.id('link-2')).click();
19743                   expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19744                   expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19745                 });
19746
19747                 it('should execute ng-click and change url when ng-href specified', function() {
19748                   expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19749
19750                   element(by.id('link-3')).click();
19751
19752                   // At this point, we navigate away from an Angular page, so we need
19753                   // to use browser.driver to get the base webdriver.
19754
19755                   browser.wait(function() {
19756                     return browser.driver.getCurrentUrl().then(function(url) {
19757                       return url.match(/\/123$/);
19758                     });
19759                   }, 5000, 'page should navigate to /123');
19760                 });
19761
19762                 it('should execute ng-click but not reload when href empty string and name specified', function() {
19763                   element(by.id('link-4')).click();
19764                   expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19765                   expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19766                 });
19767
19768                 it('should execute ng-click but not reload when no href but name specified', function() {
19769                   element(by.id('link-5')).click();
19770                   expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19771                   expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19772                 });
19773
19774                 it('should only change url when only ng-href', function() {
19775                   element(by.model('value')).clear();
19776                   element(by.model('value')).sendKeys('6');
19777                   expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19778
19779                   element(by.id('link-6')).click();
19780
19781                   // At this point, we navigate away from an Angular page, so we need
19782                   // to use browser.driver to get the base webdriver.
19783                   browser.wait(function() {
19784                     return browser.driver.getCurrentUrl().then(function(url) {
19785                       return url.match(/\/6$/);
19786                     });
19787                   }, 5000, 'page should navigate to /6');
19788                 });
19789               </file>
19790             </example>
19791          */
19792
19793         /**
19794          * @ngdoc directive
19795          * @name ngSrc
19796          * @restrict A
19797          * @priority 99
19798          *
19799          * @description
19800          * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19801          * work right: The browser will fetch from the URL with the literal
19802          * text `{{hash}}` until Angular replaces the expression inside
19803          * `{{hash}}`. The `ngSrc` directive solves this problem.
19804          *
19805          * The buggy way to write it:
19806          * ```html
19807          * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19808          * ```
19809          *
19810          * The correct way to write it:
19811          * ```html
19812          * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19813          * ```
19814          *
19815          * @element IMG
19816          * @param {template} ngSrc any string which can contain `{{}}` markup.
19817          */
19818
19819         /**
19820          * @ngdoc directive
19821          * @name ngSrcset
19822          * @restrict A
19823          * @priority 99
19824          *
19825          * @description
19826          * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19827          * work right: The browser will fetch from the URL with the literal
19828          * text `{{hash}}` until Angular replaces the expression inside
19829          * `{{hash}}`. The `ngSrcset` directive solves this problem.
19830          *
19831          * The buggy way to write it:
19832          * ```html
19833          * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19834          * ```
19835          *
19836          * The correct way to write it:
19837          * ```html
19838          * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19839          * ```
19840          *
19841          * @element IMG
19842          * @param {template} ngSrcset any string which can contain `{{}}` markup.
19843          */
19844
19845         /**
19846          * @ngdoc directive
19847          * @name ngDisabled
19848          * @restrict A
19849          * @priority 100
19850          *
19851          * @description
19852          *
19853          * This directive sets the `disabled` attribute on the element if the
19854          * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19855          *
19856          * A special directive is necessary because we cannot use interpolation inside the `disabled`
19857          * attribute.  The following example would make the button enabled on Chrome/Firefox
19858          * but not on older IEs:
19859          *
19860          * ```html
19861          * <!-- See below for an example of ng-disabled being used correctly -->
19862          * <div ng-init="isDisabled = false">
19863          *  <button disabled="{{isDisabled}}">Disabled</button>
19864          * </div>
19865          * ```
19866          *
19867          * This is because the HTML specification does not require browsers to preserve the values of
19868          * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19869          * If we put an Angular interpolation expression into such an attribute then the
19870          * binding information would be lost when the browser removes the attribute.
19871          *
19872          * @example
19873             <example>
19874               <file name="index.html">
19875                 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19876                 <button ng-model="button" ng-disabled="checked">Button</button>
19877               </file>
19878               <file name="protractor.js" type="protractor">
19879                 it('should toggle button', function() {
19880                   expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19881                   element(by.model('checked')).click();
19882                   expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19883                 });
19884               </file>
19885             </example>
19886          *
19887          * @element INPUT
19888          * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19889          *     then the `disabled` attribute will be set on the element
19890          */
19891
19892
19893         /**
19894          * @ngdoc directive
19895          * @name ngChecked
19896          * @restrict A
19897          * @priority 100
19898          *
19899          * @description
19900          * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19901          *
19902          * Note that this directive should not be used together with {@link ngModel `ngModel`},
19903          * as this can lead to unexpected behavior.
19904          *
19905          * ### Why do we need `ngChecked`?
19906          *
19907          * The HTML specification does not require browsers to preserve the values of boolean attributes
19908          * such as checked. (Their presence means true and their absence means false.)
19909          * If we put an Angular interpolation expression into such an attribute then the
19910          * binding information would be lost when the browser removes the attribute.
19911          * The `ngChecked` directive solves this problem for the `checked` attribute.
19912          * This complementary directive is not removed by the browser and so provides
19913          * a permanent reliable place to store the binding information.
19914          * @example
19915             <example>
19916               <file name="index.html">
19917                 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19918                 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19919               </file>
19920               <file name="protractor.js" type="protractor">
19921                 it('should check both checkBoxes', function() {
19922                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19923                   element(by.model('master')).click();
19924                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19925                 });
19926               </file>
19927             </example>
19928          *
19929          * @element INPUT
19930          * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19931          *     then the `checked` attribute will be set on the element
19932          */
19933
19934
19935         /**
19936          * @ngdoc directive
19937          * @name ngReadonly
19938          * @restrict A
19939          * @priority 100
19940          *
19941          * @description
19942          * The HTML specification does not require browsers to preserve the values of boolean attributes
19943          * such as readonly. (Their presence means true and their absence means false.)
19944          * If we put an Angular interpolation expression into such an attribute then the
19945          * binding information would be lost when the browser removes the attribute.
19946          * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19947          * This complementary directive is not removed by the browser and so provides
19948          * a permanent reliable place to store the binding information.
19949          * @example
19950             <example>
19951               <file name="index.html">
19952                 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19953                 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19954               </file>
19955               <file name="protractor.js" type="protractor">
19956                 it('should toggle readonly attr', function() {
19957                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19958                   element(by.model('checked')).click();
19959                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19960                 });
19961               </file>
19962             </example>
19963          *
19964          * @element INPUT
19965          * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19966          *     then special attribute "readonly" will be set on the element
19967          */
19968
19969
19970         /**
19971          * @ngdoc directive
19972          * @name ngSelected
19973          * @restrict A
19974          * @priority 100
19975          *
19976          * @description
19977          * The HTML specification does not require browsers to preserve the values of boolean attributes
19978          * such as selected. (Their presence means true and their absence means false.)
19979          * If we put an Angular interpolation expression into such an attribute then the
19980          * binding information would be lost when the browser removes the attribute.
19981          * The `ngSelected` directive solves this problem for the `selected` attribute.
19982          * This complementary directive is not removed by the browser and so provides
19983          * a permanent reliable place to store the binding information.
19984          *
19985          * @example
19986             <example>
19987               <file name="index.html">
19988                 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19989                 <select aria-label="ngSelected demo">
19990                   <option>Hello!</option>
19991                   <option id="greet" ng-selected="selected">Greetings!</option>
19992                 </select>
19993               </file>
19994               <file name="protractor.js" type="protractor">
19995                 it('should select Greetings!', function() {
19996                   expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19997                   element(by.model('selected')).click();
19998                   expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19999                 });
20000               </file>
20001             </example>
20002          *
20003          * @element OPTION
20004          * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20005          *     then special attribute "selected" will be set on the element
20006          */
20007
20008         /**
20009          * @ngdoc directive
20010          * @name ngOpen
20011          * @restrict A
20012          * @priority 100
20013          *
20014          * @description
20015          * The HTML specification does not require browsers to preserve the values of boolean attributes
20016          * such as open. (Their presence means true and their absence means false.)
20017          * If we put an Angular interpolation expression into such an attribute then the
20018          * binding information would be lost when the browser removes the attribute.
20019          * The `ngOpen` directive solves this problem for the `open` attribute.
20020          * This complementary directive is not removed by the browser and so provides
20021          * a permanent reliable place to store the binding information.
20022          * @example
20023              <example>
20024                <file name="index.html">
20025                  <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20026                  <details id="details" ng-open="open">
20027                     <summary>Show/Hide me</summary>
20028                  </details>
20029                </file>
20030                <file name="protractor.js" type="protractor">
20031                  it('should toggle open', function() {
20032                    expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20033                    element(by.model('open')).click();
20034                    expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20035                  });
20036                </file>
20037              </example>
20038          *
20039          * @element DETAILS
20040          * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20041          *     then special attribute "open" will be set on the element
20042          */
20043
20044         var ngAttributeAliasDirectives = {};
20045
20046         // boolean attrs are evaluated
20047         forEach(BOOLEAN_ATTR, function(propName, attrName) {
20048           // binding to multiple is not supported
20049           if (propName == "multiple") return;
20050
20051           function defaultLinkFn(scope, element, attr) {
20052             scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20053               attr.$set(attrName, !!value);
20054             });
20055           }
20056
20057           var normalized = directiveNormalize('ng-' + attrName);
20058           var linkFn = defaultLinkFn;
20059
20060           if (propName === 'checked') {
20061             linkFn = function(scope, element, attr) {
20062               // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20063               if (attr.ngModel !== attr[normalized]) {
20064                 defaultLinkFn(scope, element, attr);
20065               }
20066             };
20067           }
20068
20069           ngAttributeAliasDirectives[normalized] = function() {
20070             return {
20071               restrict: 'A',
20072               priority: 100,
20073               link: linkFn
20074             };
20075           };
20076         });
20077
20078         // aliased input attrs are evaluated
20079         forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20080           ngAttributeAliasDirectives[ngAttr] = function() {
20081             return {
20082               priority: 100,
20083               link: function(scope, element, attr) {
20084                 //special case ngPattern when a literal regular expression value
20085                 //is used as the expression (this way we don't have to watch anything).
20086                 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20087                   var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20088                   if (match) {
20089                     attr.$set("ngPattern", new RegExp(match[1], match[2]));
20090                     return;
20091                   }
20092                 }
20093
20094                 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20095                   attr.$set(ngAttr, value);
20096                 });
20097               }
20098             };
20099           };
20100         });
20101
20102         // ng-src, ng-srcset, ng-href are interpolated
20103         forEach(['src', 'srcset', 'href'], function(attrName) {
20104           var normalized = directiveNormalize('ng-' + attrName);
20105           ngAttributeAliasDirectives[normalized] = function() {
20106             return {
20107               priority: 99, // it needs to run after the attributes are interpolated
20108               link: function(scope, element, attr) {
20109                 var propName = attrName,
20110                     name = attrName;
20111
20112                 if (attrName === 'href' &&
20113                     toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20114                   name = 'xlinkHref';
20115                   attr.$attr[name] = 'xlink:href';
20116                   propName = null;
20117                 }
20118
20119                 attr.$observe(normalized, function(value) {
20120                   if (!value) {
20121                     if (attrName === 'href') {
20122                       attr.$set(name, null);
20123                     }
20124                     return;
20125                   }
20126
20127                   attr.$set(name, value);
20128
20129                   // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20130                   // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20131                   // to set the property as well to achieve the desired effect.
20132                   // we use attr[attrName] value since $set can sanitize the url.
20133                   if (msie && propName) element.prop(propName, attr[name]);
20134                 });
20135               }
20136             };
20137           };
20138         });
20139
20140         /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20141          */
20142         var nullFormCtrl = {
20143           $addControl: noop,
20144           $$renameControl: nullFormRenameControl,
20145           $removeControl: noop,
20146           $setValidity: noop,
20147           $setDirty: noop,
20148           $setPristine: noop,
20149           $setSubmitted: noop
20150         },
20151         SUBMITTED_CLASS = 'ng-submitted';
20152
20153         function nullFormRenameControl(control, name) {
20154           control.$name = name;
20155         }
20156
20157         /**
20158          * @ngdoc type
20159          * @name form.FormController
20160          *
20161          * @property {boolean} $pristine True if user has not interacted with the form yet.
20162          * @property {boolean} $dirty True if user has already interacted with the form.
20163          * @property {boolean} $valid True if all of the containing forms and controls are valid.
20164          * @property {boolean} $invalid True if at least one containing control or form is invalid.
20165          * @property {boolean} $pending True if at least one containing control or form is pending.
20166          * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20167          *
20168          * @property {Object} $error Is an object hash, containing references to controls or
20169          *  forms with failing validators, where:
20170          *
20171          *  - keys are validation tokens (error names),
20172          *  - values are arrays of controls or forms that have a failing validator for given error name.
20173          *
20174          *  Built-in validation tokens:
20175          *
20176          *  - `email`
20177          *  - `max`
20178          *  - `maxlength`
20179          *  - `min`
20180          *  - `minlength`
20181          *  - `number`
20182          *  - `pattern`
20183          *  - `required`
20184          *  - `url`
20185          *  - `date`
20186          *  - `datetimelocal`
20187          *  - `time`
20188          *  - `week`
20189          *  - `month`
20190          *
20191          * @description
20192          * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20193          * such as being valid/invalid or dirty/pristine.
20194          *
20195          * Each {@link ng.directive:form form} directive creates an instance
20196          * of `FormController`.
20197          *
20198          */
20199         //asks for $scope to fool the BC controller module
20200         FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20201         function FormController(element, attrs, $scope, $animate, $interpolate) {
20202           var form = this,
20203               controls = [];
20204
20205           // init state
20206           form.$error = {};
20207           form.$$success = {};
20208           form.$pending = undefined;
20209           form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20210           form.$dirty = false;
20211           form.$pristine = true;
20212           form.$valid = true;
20213           form.$invalid = false;
20214           form.$submitted = false;
20215           form.$$parentForm = nullFormCtrl;
20216
20217           /**
20218            * @ngdoc method
20219            * @name form.FormController#$rollbackViewValue
20220            *
20221            * @description
20222            * Rollback all form controls pending updates to the `$modelValue`.
20223            *
20224            * Updates may be pending by a debounced event or because the input is waiting for a some future
20225            * event defined in `ng-model-options`. This method is typically needed by the reset button of
20226            * a form that uses `ng-model-options` to pend updates.
20227            */
20228           form.$rollbackViewValue = function() {
20229             forEach(controls, function(control) {
20230               control.$rollbackViewValue();
20231             });
20232           };
20233
20234           /**
20235            * @ngdoc method
20236            * @name form.FormController#$commitViewValue
20237            *
20238            * @description
20239            * Commit all form controls pending updates to the `$modelValue`.
20240            *
20241            * Updates may be pending by a debounced event or because the input is waiting for a some future
20242            * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20243            * usually handles calling this in response to input events.
20244            */
20245           form.$commitViewValue = function() {
20246             forEach(controls, function(control) {
20247               control.$commitViewValue();
20248             });
20249           };
20250
20251           /**
20252            * @ngdoc method
20253            * @name form.FormController#$addControl
20254            * @param {object} control control object, either a {@link form.FormController} or an
20255            * {@link ngModel.NgModelController}
20256            *
20257            * @description
20258            * Register a control with the form. Input elements using ngModelController do this automatically
20259            * when they are linked.
20260            *
20261            * Note that the current state of the control will not be reflected on the new parent form. This
20262            * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20263            * state.
20264            *
20265            * However, if the method is used programmatically, for example by adding dynamically created controls,
20266            * or controls that have been previously removed without destroying their corresponding DOM element,
20267            * it's the developers responsiblity to make sure the current state propagates to the parent form.
20268            *
20269            * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20270            * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20271            */
20272           form.$addControl = function(control) {
20273             // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20274             // and not added to the scope.  Now we throw an error.
20275             assertNotHasOwnProperty(control.$name, 'input');
20276             controls.push(control);
20277
20278             if (control.$name) {
20279               form[control.$name] = control;
20280             }
20281
20282             control.$$parentForm = form;
20283           };
20284
20285           // Private API: rename a form control
20286           form.$$renameControl = function(control, newName) {
20287             var oldName = control.$name;
20288
20289             if (form[oldName] === control) {
20290               delete form[oldName];
20291             }
20292             form[newName] = control;
20293             control.$name = newName;
20294           };
20295
20296           /**
20297            * @ngdoc method
20298            * @name form.FormController#$removeControl
20299            * @param {object} control control object, either a {@link form.FormController} or an
20300            * {@link ngModel.NgModelController}
20301            *
20302            * @description
20303            * Deregister a control from the form.
20304            *
20305            * Input elements using ngModelController do this automatically when they are destroyed.
20306            *
20307            * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20308            * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20309            * different from case to case. For example, removing the only `$dirty` control from a form may or
20310            * may not mean that the form is still `$dirty`.
20311            */
20312           form.$removeControl = function(control) {
20313             if (control.$name && form[control.$name] === control) {
20314               delete form[control.$name];
20315             }
20316             forEach(form.$pending, function(value, name) {
20317               form.$setValidity(name, null, control);
20318             });
20319             forEach(form.$error, function(value, name) {
20320               form.$setValidity(name, null, control);
20321             });
20322             forEach(form.$$success, function(value, name) {
20323               form.$setValidity(name, null, control);
20324             });
20325
20326             arrayRemove(controls, control);
20327             control.$$parentForm = nullFormCtrl;
20328           };
20329
20330
20331           /**
20332            * @ngdoc method
20333            * @name form.FormController#$setValidity
20334            *
20335            * @description
20336            * Sets the validity of a form control.
20337            *
20338            * This method will also propagate to parent forms.
20339            */
20340           addSetValidityMethod({
20341             ctrl: this,
20342             $element: element,
20343             set: function(object, property, controller) {
20344               var list = object[property];
20345               if (!list) {
20346                 object[property] = [controller];
20347               } else {
20348                 var index = list.indexOf(controller);
20349                 if (index === -1) {
20350                   list.push(controller);
20351                 }
20352               }
20353             },
20354             unset: function(object, property, controller) {
20355               var list = object[property];
20356               if (!list) {
20357                 return;
20358               }
20359               arrayRemove(list, controller);
20360               if (list.length === 0) {
20361                 delete object[property];
20362               }
20363             },
20364             $animate: $animate
20365           });
20366
20367           /**
20368            * @ngdoc method
20369            * @name form.FormController#$setDirty
20370            *
20371            * @description
20372            * Sets the form to a dirty state.
20373            *
20374            * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20375            * state (ng-dirty class). This method will also propagate to parent forms.
20376            */
20377           form.$setDirty = function() {
20378             $animate.removeClass(element, PRISTINE_CLASS);
20379             $animate.addClass(element, DIRTY_CLASS);
20380             form.$dirty = true;
20381             form.$pristine = false;
20382             form.$$parentForm.$setDirty();
20383           };
20384
20385           /**
20386            * @ngdoc method
20387            * @name form.FormController#$setPristine
20388            *
20389            * @description
20390            * Sets the form to its pristine state.
20391            *
20392            * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20393            * state (ng-pristine class). This method will also propagate to all the controls contained
20394            * in this form.
20395            *
20396            * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20397            * saving or resetting it.
20398            */
20399           form.$setPristine = function() {
20400             $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20401             form.$dirty = false;
20402             form.$pristine = true;
20403             form.$submitted = false;
20404             forEach(controls, function(control) {
20405               control.$setPristine();
20406             });
20407           };
20408
20409           /**
20410            * @ngdoc method
20411            * @name form.FormController#$setUntouched
20412            *
20413            * @description
20414            * Sets the form to its untouched state.
20415            *
20416            * This method can be called to remove the 'ng-touched' class and set the form controls to their
20417            * untouched state (ng-untouched class).
20418            *
20419            * Setting a form controls back to their untouched state is often useful when setting the form
20420            * back to its pristine state.
20421            */
20422           form.$setUntouched = function() {
20423             forEach(controls, function(control) {
20424               control.$setUntouched();
20425             });
20426           };
20427
20428           /**
20429            * @ngdoc method
20430            * @name form.FormController#$setSubmitted
20431            *
20432            * @description
20433            * Sets the form to its submitted state.
20434            */
20435           form.$setSubmitted = function() {
20436             $animate.addClass(element, SUBMITTED_CLASS);
20437             form.$submitted = true;
20438             form.$$parentForm.$setSubmitted();
20439           };
20440         }
20441
20442         /**
20443          * @ngdoc directive
20444          * @name ngForm
20445          * @restrict EAC
20446          *
20447          * @description
20448          * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20449          * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20450          * sub-group of controls needs to be determined.
20451          *
20452          * Note: the purpose of `ngForm` is to group controls,
20453          * but not to be a replacement for the `<form>` tag with all of its capabilities
20454          * (e.g. posting to the server, ...).
20455          *
20456          * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20457          *                       related scope, under this name.
20458          *
20459          */
20460
20461          /**
20462          * @ngdoc directive
20463          * @name form
20464          * @restrict E
20465          *
20466          * @description
20467          * Directive that instantiates
20468          * {@link form.FormController FormController}.
20469          *
20470          * If the `name` attribute is specified, the form controller is published onto the current scope under
20471          * this name.
20472          *
20473          * # Alias: {@link ng.directive:ngForm `ngForm`}
20474          *
20475          * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20476          * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20477          * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20478          * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20479          * using Angular validation directives in forms that are dynamically generated using the
20480          * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20481          * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20482          * `ngForm` directive and nest these in an outer `form` element.
20483          *
20484          *
20485          * # CSS classes
20486          *  - `ng-valid` is set if the form is valid.
20487          *  - `ng-invalid` is set if the form is invalid.
20488          *  - `ng-pending` is set if the form is pending.
20489          *  - `ng-pristine` is set if the form is pristine.
20490          *  - `ng-dirty` is set if the form is dirty.
20491          *  - `ng-submitted` is set if the form was submitted.
20492          *
20493          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20494          *
20495          *
20496          * # Submitting a form and preventing the default action
20497          *
20498          * Since the role of forms in client-side Angular applications is different than in classical
20499          * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20500          * page reload that sends the data to the server. Instead some javascript logic should be triggered
20501          * to handle the form submission in an application-specific way.
20502          *
20503          * For this reason, Angular prevents the default action (form submission to the server) unless the
20504          * `<form>` element has an `action` attribute specified.
20505          *
20506          * You can use one of the following two ways to specify what javascript method should be called when
20507          * a form is submitted:
20508          *
20509          * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20510          * - {@link ng.directive:ngClick ngClick} directive on the first
20511           *  button or input field of type submit (input[type=submit])
20512          *
20513          * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20514          * or {@link ng.directive:ngClick ngClick} directives.
20515          * This is because of the following form submission rules in the HTML specification:
20516          *
20517          * - If a form has only one input field then hitting enter in this field triggers form submit
20518          * (`ngSubmit`)
20519          * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20520          * doesn't trigger submit
20521          * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20522          * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20523          * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20524          *
20525          * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20526          * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20527          * to have access to the updated model.
20528          *
20529          * ## Animation Hooks
20530          *
20531          * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20532          * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20533          * other validations that are performed within the form. Animations in ngForm are similar to how
20534          * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20535          * as JS animations.
20536          *
20537          * The following example shows a simple way to utilize CSS transitions to style a form element
20538          * that has been rendered as invalid after it has been validated:
20539          *
20540          * <pre>
20541          * //be sure to include ngAnimate as a module to hook into more
20542          * //advanced animations
20543          * .my-form {
20544          *   transition:0.5s linear all;
20545          *   background: white;
20546          * }
20547          * .my-form.ng-invalid {
20548          *   background: red;
20549          *   color:white;
20550          * }
20551          * </pre>
20552          *
20553          * @example
20554             <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20555               <file name="index.html">
20556                <script>
20557                  angular.module('formExample', [])
20558                    .controller('FormController', ['$scope', function($scope) {
20559                      $scope.userType = 'guest';
20560                    }]);
20561                </script>
20562                <style>
20563                 .my-form {
20564                   transition:all linear 0.5s;
20565                   background: transparent;
20566                 }
20567                 .my-form.ng-invalid {
20568                   background: red;
20569                 }
20570                </style>
20571                <form name="myForm" ng-controller="FormController" class="my-form">
20572                  userType: <input name="input" ng-model="userType" required>
20573                  <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20574                  <code>userType = {{userType}}</code><br>
20575                  <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20576                  <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20577                  <code>myForm.$valid = {{myForm.$valid}}</code><br>
20578                  <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20579                 </form>
20580               </file>
20581               <file name="protractor.js" type="protractor">
20582                 it('should initialize to model', function() {
20583                   var userType = element(by.binding('userType'));
20584                   var valid = element(by.binding('myForm.input.$valid'));
20585
20586                   expect(userType.getText()).toContain('guest');
20587                   expect(valid.getText()).toContain('true');
20588                 });
20589
20590                 it('should be invalid if empty', function() {
20591                   var userType = element(by.binding('userType'));
20592                   var valid = element(by.binding('myForm.input.$valid'));
20593                   var userInput = element(by.model('userType'));
20594
20595                   userInput.clear();
20596                   userInput.sendKeys('');
20597
20598                   expect(userType.getText()).toEqual('userType =');
20599                   expect(valid.getText()).toContain('false');
20600                 });
20601               </file>
20602             </example>
20603          *
20604          * @param {string=} name Name of the form. If specified, the form controller will be published into
20605          *                       related scope, under this name.
20606          */
20607         var formDirectiveFactory = function(isNgForm) {
20608           return ['$timeout', '$parse', function($timeout, $parse) {
20609             var formDirective = {
20610               name: 'form',
20611               restrict: isNgForm ? 'EAC' : 'E',
20612               require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20613               controller: FormController,
20614               compile: function ngFormCompile(formElement, attr) {
20615                 // Setup initial state of the control
20616                 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20617
20618                 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20619
20620                 return {
20621                   pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20622                     var controller = ctrls[0];
20623
20624                     // if `action` attr is not present on the form, prevent the default action (submission)
20625                     if (!('action' in attr)) {
20626                       // we can't use jq events because if a form is destroyed during submission the default
20627                       // action is not prevented. see #1238
20628                       //
20629                       // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20630                       // page reload if the form was destroyed by submission of the form via a click handler
20631                       // on a button in the form. Looks like an IE9 specific bug.
20632                       var handleFormSubmission = function(event) {
20633                         scope.$apply(function() {
20634                           controller.$commitViewValue();
20635                           controller.$setSubmitted();
20636                         });
20637
20638                         event.preventDefault();
20639                       };
20640
20641                       addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20642
20643                       // unregister the preventDefault listener so that we don't not leak memory but in a
20644                       // way that will achieve the prevention of the default action.
20645                       formElement.on('$destroy', function() {
20646                         $timeout(function() {
20647                           removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20648                         }, 0, false);
20649                       });
20650                     }
20651
20652                     var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20653                     parentFormCtrl.$addControl(controller);
20654
20655                     var setter = nameAttr ? getSetter(controller.$name) : noop;
20656
20657                     if (nameAttr) {
20658                       setter(scope, controller);
20659                       attr.$observe(nameAttr, function(newValue) {
20660                         if (controller.$name === newValue) return;
20661                         setter(scope, undefined);
20662                         controller.$$parentForm.$$renameControl(controller, newValue);
20663                         setter = getSetter(controller.$name);
20664                         setter(scope, controller);
20665                       });
20666                     }
20667                     formElement.on('$destroy', function() {
20668                       controller.$$parentForm.$removeControl(controller);
20669                       setter(scope, undefined);
20670                       extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20671                     });
20672                   }
20673                 };
20674               }
20675             };
20676
20677             return formDirective;
20678
20679             function getSetter(expression) {
20680               if (expression === '') {
20681                 //create an assignable expression, so forms with an empty name can be renamed later
20682                 return $parse('this[""]').assign;
20683               }
20684               return $parse(expression).assign || noop;
20685             }
20686           }];
20687         };
20688
20689         var formDirective = formDirectiveFactory();
20690         var ngFormDirective = formDirectiveFactory(true);
20691
20692         /* global VALID_CLASS: false,
20693           INVALID_CLASS: false,
20694           PRISTINE_CLASS: false,
20695           DIRTY_CLASS: false,
20696           UNTOUCHED_CLASS: false,
20697           TOUCHED_CLASS: false,
20698           ngModelMinErr: false,
20699         */
20700
20701         // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20702         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)/;
20703         // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20704         var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20705         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;
20706         var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20707         var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20708         var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20709         var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20710         var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20711         var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20712
20713         var inputType = {
20714
20715           /**
20716            * @ngdoc input
20717            * @name input[text]
20718            *
20719            * @description
20720            * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20721            *
20722            *
20723            * @param {string} ngModel Assignable angular expression to data-bind to.
20724            * @param {string=} name Property name of the form under which the control is published.
20725            * @param {string=} required Adds `required` validation error key if the value is not entered.
20726            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20727            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20728            *    `required` when you want to data-bind to the `required` attribute.
20729            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20730            *    minlength.
20731            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20732            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20733            *    any length.
20734            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20735            *    that contains the regular expression body that will be converted to a regular expression
20736            *    as in the ngPattern directive.
20737            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20738            *    a RegExp found by evaluating the Angular expression given in the attribute value.
20739            *    If the expression evaluates to a RegExp object, then this is used directly.
20740            *    If the expression evaluates to a string, then it will be converted to a RegExp
20741            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20742            *    `new RegExp('^abc$')`.<br />
20743            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20744            *    start at the index of the last search's match, thus not taking the whole input value into
20745            *    account.
20746            * @param {string=} ngChange Angular expression to be executed when input changes due to user
20747            *    interaction with the input element.
20748            * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20749            *    This parameter is ignored for input[type=password] controls, which will never trim the
20750            *    input.
20751            *
20752            * @example
20753               <example name="text-input-directive" module="textInputExample">
20754                 <file name="index.html">
20755                  <script>
20756                    angular.module('textInputExample', [])
20757                      .controller('ExampleController', ['$scope', function($scope) {
20758                        $scope.example = {
20759                          text: 'guest',
20760                          word: /^\s*\w*\s*$/
20761                        };
20762                      }]);
20763                  </script>
20764                  <form name="myForm" ng-controller="ExampleController">
20765                    <label>Single word:
20766                      <input type="text" name="input" ng-model="example.text"
20767                             ng-pattern="example.word" required ng-trim="false">
20768                    </label>
20769                    <div role="alert">
20770                      <span class="error" ng-show="myForm.input.$error.required">
20771                        Required!</span>
20772                      <span class="error" ng-show="myForm.input.$error.pattern">
20773                        Single word only!</span>
20774                    </div>
20775                    <tt>text = {{example.text}}</tt><br/>
20776                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20777                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20778                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20779                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20780                   </form>
20781                 </file>
20782                 <file name="protractor.js" type="protractor">
20783                   var text = element(by.binding('example.text'));
20784                   var valid = element(by.binding('myForm.input.$valid'));
20785                   var input = element(by.model('example.text'));
20786
20787                   it('should initialize to model', function() {
20788                     expect(text.getText()).toContain('guest');
20789                     expect(valid.getText()).toContain('true');
20790                   });
20791
20792                   it('should be invalid if empty', function() {
20793                     input.clear();
20794                     input.sendKeys('');
20795
20796                     expect(text.getText()).toEqual('text =');
20797                     expect(valid.getText()).toContain('false');
20798                   });
20799
20800                   it('should be invalid if multi word', function() {
20801                     input.clear();
20802                     input.sendKeys('hello world');
20803
20804                     expect(valid.getText()).toContain('false');
20805                   });
20806                 </file>
20807               </example>
20808            */
20809           'text': textInputType,
20810
20811             /**
20812              * @ngdoc input
20813              * @name input[date]
20814              *
20815              * @description
20816              * Input with date validation and transformation. In browsers that do not yet support
20817              * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20818              * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20819              * modern browsers do not yet support this input type, it is important to provide cues to users on the
20820              * expected input format via a placeholder or label.
20821              *
20822              * The model must always be a Date object, otherwise Angular will throw an error.
20823              * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20824              *
20825              * The timezone to be used to read/write the `Date` instance in the model can be defined using
20826              * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20827              *
20828              * @param {string} ngModel Assignable angular expression to data-bind to.
20829              * @param {string=} name Property name of the form under which the control is published.
20830              * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20831              *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20832              *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20833              *   constraint validation.
20834              * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20835              *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20836              *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20837              *   constraint validation.
20838              * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20839              *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20840              * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20841              *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20842              * @param {string=} required Sets `required` validation error key if the value is not entered.
20843              * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20844              *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20845              *    `required` when you want to data-bind to the `required` attribute.
20846              * @param {string=} ngChange Angular expression to be executed when input changes due to user
20847              *    interaction with the input element.
20848              *
20849              * @example
20850              <example name="date-input-directive" module="dateInputExample">
20851              <file name="index.html">
20852                <script>
20853                   angular.module('dateInputExample', [])
20854                     .controller('DateController', ['$scope', function($scope) {
20855                       $scope.example = {
20856                         value: new Date(2013, 9, 22)
20857                       };
20858                     }]);
20859                </script>
20860                <form name="myForm" ng-controller="DateController as dateCtrl">
20861                   <label for="exampleInput">Pick a date in 2013:</label>
20862                   <input type="date" id="exampleInput" name="input" ng-model="example.value"
20863                       placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20864                   <div role="alert">
20865                     <span class="error" ng-show="myForm.input.$error.required">
20866                         Required!</span>
20867                     <span class="error" ng-show="myForm.input.$error.date">
20868                         Not a valid date!</span>
20869                    </div>
20870                    <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20871                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20872                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20873                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20874                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20875                </form>
20876              </file>
20877              <file name="protractor.js" type="protractor">
20878                 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20879                 var valid = element(by.binding('myForm.input.$valid'));
20880                 var input = element(by.model('example.value'));
20881
20882                 // currently protractor/webdriver does not support
20883                 // sending keys to all known HTML5 input controls
20884                 // for various browsers (see https://github.com/angular/protractor/issues/562).
20885                 function setInput(val) {
20886                   // set the value of the element and force validation.
20887                   var scr = "var ipt = document.getElementById('exampleInput'); " +
20888                   "ipt.value = '" + val + "';" +
20889                   "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20890                   browser.executeScript(scr);
20891                 }
20892
20893                 it('should initialize to model', function() {
20894                   expect(value.getText()).toContain('2013-10-22');
20895                   expect(valid.getText()).toContain('myForm.input.$valid = true');
20896                 });
20897
20898                 it('should be invalid if empty', function() {
20899                   setInput('');
20900                   expect(value.getText()).toEqual('value =');
20901                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20902                 });
20903
20904                 it('should be invalid if over max', function() {
20905                   setInput('2015-01-01');
20906                   expect(value.getText()).toContain('');
20907                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20908                 });
20909              </file>
20910              </example>
20911              */
20912           'date': createDateInputType('date', DATE_REGEXP,
20913                  createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20914                  'yyyy-MM-dd'),
20915
20916            /**
20917             * @ngdoc input
20918             * @name input[datetime-local]
20919             *
20920             * @description
20921             * Input with datetime validation and transformation. In browsers that do not yet support
20922             * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20923             * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20924             *
20925             * The model must always be a Date object, otherwise Angular will throw an error.
20926             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20927             *
20928             * The timezone to be used to read/write the `Date` instance in the model can be defined using
20929             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20930             *
20931             * @param {string} ngModel Assignable angular expression to data-bind to.
20932             * @param {string=} name Property name of the form under which the control is published.
20933             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20934             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20935             *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20936             *   Note that `min` will also add native HTML5 constraint validation.
20937             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20938             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20939             *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20940             *   Note that `max` will also add native HTML5 constraint validation.
20941             * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20942             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20943             * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20944             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20945             * @param {string=} required Sets `required` validation error key if the value is not entered.
20946             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20947             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20948             *    `required` when you want to data-bind to the `required` attribute.
20949             * @param {string=} ngChange Angular expression to be executed when input changes due to user
20950             *    interaction with the input element.
20951             *
20952             * @example
20953             <example name="datetimelocal-input-directive" module="dateExample">
20954             <file name="index.html">
20955               <script>
20956                 angular.module('dateExample', [])
20957                   .controller('DateController', ['$scope', function($scope) {
20958                     $scope.example = {
20959                       value: new Date(2010, 11, 28, 14, 57)
20960                     };
20961                   }]);
20962               </script>
20963               <form name="myForm" ng-controller="DateController as dateCtrl">
20964                 <label for="exampleInput">Pick a date between in 2013:</label>
20965                 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20966                     placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20967                 <div role="alert">
20968                   <span class="error" ng-show="myForm.input.$error.required">
20969                       Required!</span>
20970                   <span class="error" ng-show="myForm.input.$error.datetimelocal">
20971                       Not a valid date!</span>
20972                 </div>
20973                 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20974                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20975                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20976                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20977                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20978               </form>
20979             </file>
20980             <file name="protractor.js" type="protractor">
20981               var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20982               var valid = element(by.binding('myForm.input.$valid'));
20983               var input = element(by.model('example.value'));
20984
20985               // currently protractor/webdriver does not support
20986               // sending keys to all known HTML5 input controls
20987               // for various browsers (https://github.com/angular/protractor/issues/562).
20988               function setInput(val) {
20989                 // set the value of the element and force validation.
20990                 var scr = "var ipt = document.getElementById('exampleInput'); " +
20991                 "ipt.value = '" + val + "';" +
20992                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20993                 browser.executeScript(scr);
20994               }
20995
20996               it('should initialize to model', function() {
20997                 expect(value.getText()).toContain('2010-12-28T14:57:00');
20998                 expect(valid.getText()).toContain('myForm.input.$valid = true');
20999               });
21000
21001               it('should be invalid if empty', function() {
21002                 setInput('');
21003                 expect(value.getText()).toEqual('value =');
21004                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21005               });
21006
21007               it('should be invalid if over max', function() {
21008                 setInput('2015-01-01T23:59:00');
21009                 expect(value.getText()).toContain('');
21010                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21011               });
21012             </file>
21013             </example>
21014             */
21015           'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21016               createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21017               'yyyy-MM-ddTHH:mm:ss.sss'),
21018
21019           /**
21020            * @ngdoc input
21021            * @name input[time]
21022            *
21023            * @description
21024            * Input with time validation and transformation. In browsers that do not yet support
21025            * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21026            * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21027            * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21028            *
21029            * The model must always be a Date object, otherwise Angular will throw an error.
21030            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21031            *
21032            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21033            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21034            *
21035            * @param {string} ngModel Assignable angular expression to data-bind to.
21036            * @param {string=} name Property name of the form under which the control is published.
21037            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21038            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21039            *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21040            *   native HTML5 constraint validation.
21041            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21042            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21043            *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21044            *   native HTML5 constraint validation.
21045            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21046            *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21047            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21048            *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21049            * @param {string=} required Sets `required` validation error key if the value is not entered.
21050            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21051            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21052            *    `required` when you want to data-bind to the `required` attribute.
21053            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21054            *    interaction with the input element.
21055            *
21056            * @example
21057            <example name="time-input-directive" module="timeExample">
21058            <file name="index.html">
21059              <script>
21060               angular.module('timeExample', [])
21061                 .controller('DateController', ['$scope', function($scope) {
21062                   $scope.example = {
21063                     value: new Date(1970, 0, 1, 14, 57, 0)
21064                   };
21065                 }]);
21066              </script>
21067              <form name="myForm" ng-controller="DateController as dateCtrl">
21068                 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21069                 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21070                     placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21071                 <div role="alert">
21072                   <span class="error" ng-show="myForm.input.$error.required">
21073                       Required!</span>
21074                   <span class="error" ng-show="myForm.input.$error.time">
21075                       Not a valid date!</span>
21076                 </div>
21077                 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21078                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21079                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21080                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21081                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21082              </form>
21083            </file>
21084            <file name="protractor.js" type="protractor">
21085               var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21086               var valid = element(by.binding('myForm.input.$valid'));
21087               var input = element(by.model('example.value'));
21088
21089               // currently protractor/webdriver does not support
21090               // sending keys to all known HTML5 input controls
21091               // for various browsers (https://github.com/angular/protractor/issues/562).
21092               function setInput(val) {
21093                 // set the value of the element and force validation.
21094                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21095                 "ipt.value = '" + val + "';" +
21096                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21097                 browser.executeScript(scr);
21098               }
21099
21100               it('should initialize to model', function() {
21101                 expect(value.getText()).toContain('14:57:00');
21102                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21103               });
21104
21105               it('should be invalid if empty', function() {
21106                 setInput('');
21107                 expect(value.getText()).toEqual('value =');
21108                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21109               });
21110
21111               it('should be invalid if over max', function() {
21112                 setInput('23:59:00');
21113                 expect(value.getText()).toContain('');
21114                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21115               });
21116            </file>
21117            </example>
21118            */
21119           'time': createDateInputType('time', TIME_REGEXP,
21120               createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21121              'HH:mm:ss.sss'),
21122
21123            /**
21124             * @ngdoc input
21125             * @name input[week]
21126             *
21127             * @description
21128             * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21129             * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21130             * week format (yyyy-W##), for example: `2013-W02`.
21131             *
21132             * The model must always be a Date object, otherwise Angular will throw an error.
21133             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21134             *
21135             * The timezone to be used to read/write the `Date` instance in the model can be defined using
21136             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21137             *
21138             * @param {string} ngModel Assignable angular expression to data-bind to.
21139             * @param {string=} name Property name of the form under which the control is published.
21140             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21141             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21142             *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21143             *   native HTML5 constraint validation.
21144             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21145             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21146             *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21147             *   native HTML5 constraint validation.
21148             * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21149             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21150             * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21151             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21152             * @param {string=} required Sets `required` validation error key if the value is not entered.
21153             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21154             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21155             *    `required` when you want to data-bind to the `required` attribute.
21156             * @param {string=} ngChange Angular expression to be executed when input changes due to user
21157             *    interaction with the input element.
21158             *
21159             * @example
21160             <example name="week-input-directive" module="weekExample">
21161             <file name="index.html">
21162               <script>
21163               angular.module('weekExample', [])
21164                 .controller('DateController', ['$scope', function($scope) {
21165                   $scope.example = {
21166                     value: new Date(2013, 0, 3)
21167                   };
21168                 }]);
21169               </script>
21170               <form name="myForm" ng-controller="DateController as dateCtrl">
21171                 <label>Pick a date between in 2013:
21172                   <input id="exampleInput" type="week" name="input" ng-model="example.value"
21173                          placeholder="YYYY-W##" min="2012-W32"
21174                          max="2013-W52" required />
21175                 </label>
21176                 <div role="alert">
21177                   <span class="error" ng-show="myForm.input.$error.required">
21178                       Required!</span>
21179                   <span class="error" ng-show="myForm.input.$error.week">
21180                       Not a valid date!</span>
21181                 </div>
21182                 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21183                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21184                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21185                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21186                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21187               </form>
21188             </file>
21189             <file name="protractor.js" type="protractor">
21190               var value = element(by.binding('example.value | date: "yyyy-Www"'));
21191               var valid = element(by.binding('myForm.input.$valid'));
21192               var input = element(by.model('example.value'));
21193
21194               // currently protractor/webdriver does not support
21195               // sending keys to all known HTML5 input controls
21196               // for various browsers (https://github.com/angular/protractor/issues/562).
21197               function setInput(val) {
21198                 // set the value of the element and force validation.
21199                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21200                 "ipt.value = '" + val + "';" +
21201                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21202                 browser.executeScript(scr);
21203               }
21204
21205               it('should initialize to model', function() {
21206                 expect(value.getText()).toContain('2013-W01');
21207                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21208               });
21209
21210               it('should be invalid if empty', function() {
21211                 setInput('');
21212                 expect(value.getText()).toEqual('value =');
21213                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21214               });
21215
21216               it('should be invalid if over max', function() {
21217                 setInput('2015-W01');
21218                 expect(value.getText()).toContain('');
21219                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21220               });
21221             </file>
21222             </example>
21223             */
21224           'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21225
21226           /**
21227            * @ngdoc input
21228            * @name input[month]
21229            *
21230            * @description
21231            * Input with month validation and transformation. In browsers that do not yet support
21232            * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21233            * month format (yyyy-MM), for example: `2009-01`.
21234            *
21235            * The model must always be a Date object, otherwise Angular will throw an error.
21236            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21237            * If the model is not set to the first of the month, the next view to model update will set it
21238            * to the first of the month.
21239            *
21240            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21241            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21242            *
21243            * @param {string} ngModel Assignable angular expression to data-bind to.
21244            * @param {string=} name Property name of the form under which the control is published.
21245            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21246            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21247            *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21248            *   native HTML5 constraint validation.
21249            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21250            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21251            *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21252            *   native HTML5 constraint validation.
21253            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21254            *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21255            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21256            *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21257
21258            * @param {string=} required Sets `required` validation error key if the value is not entered.
21259            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21260            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21261            *    `required` when you want to data-bind to the `required` attribute.
21262            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21263            *    interaction with the input element.
21264            *
21265            * @example
21266            <example name="month-input-directive" module="monthExample">
21267            <file name="index.html">
21268              <script>
21269               angular.module('monthExample', [])
21270                 .controller('DateController', ['$scope', function($scope) {
21271                   $scope.example = {
21272                     value: new Date(2013, 9, 1)
21273                   };
21274                 }]);
21275              </script>
21276              <form name="myForm" ng-controller="DateController as dateCtrl">
21277                <label for="exampleInput">Pick a month in 2013:</label>
21278                <input id="exampleInput" type="month" name="input" ng-model="example.value"
21279                   placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21280                <div role="alert">
21281                  <span class="error" ng-show="myForm.input.$error.required">
21282                     Required!</span>
21283                  <span class="error" ng-show="myForm.input.$error.month">
21284                     Not a valid month!</span>
21285                </div>
21286                <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21287                <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21288                <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21289                <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21290                <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21291              </form>
21292            </file>
21293            <file name="protractor.js" type="protractor">
21294               var value = element(by.binding('example.value | date: "yyyy-MM"'));
21295               var valid = element(by.binding('myForm.input.$valid'));
21296               var input = element(by.model('example.value'));
21297
21298               // currently protractor/webdriver does not support
21299               // sending keys to all known HTML5 input controls
21300               // for various browsers (https://github.com/angular/protractor/issues/562).
21301               function setInput(val) {
21302                 // set the value of the element and force validation.
21303                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21304                 "ipt.value = '" + val + "';" +
21305                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21306                 browser.executeScript(scr);
21307               }
21308
21309               it('should initialize to model', function() {
21310                 expect(value.getText()).toContain('2013-10');
21311                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21312               });
21313
21314               it('should be invalid if empty', function() {
21315                 setInput('');
21316                 expect(value.getText()).toEqual('value =');
21317                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21318               });
21319
21320               it('should be invalid if over max', function() {
21321                 setInput('2015-01');
21322                 expect(value.getText()).toContain('');
21323                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21324               });
21325            </file>
21326            </example>
21327            */
21328           'month': createDateInputType('month', MONTH_REGEXP,
21329              createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21330              'yyyy-MM'),
21331
21332           /**
21333            * @ngdoc input
21334            * @name input[number]
21335            *
21336            * @description
21337            * Text input with number validation and transformation. Sets the `number` validation
21338            * error if not a valid number.
21339            *
21340            * <div class="alert alert-warning">
21341            * The model must always be of type `number` otherwise Angular will throw an error.
21342            * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21343            * error docs for more information and an example of how to convert your model if necessary.
21344            * </div>
21345            *
21346            * ## Issues with HTML5 constraint validation
21347            *
21348            * In browsers that follow the
21349            * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21350            * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21351            * If a non-number is entered in the input, the browser will report the value as an empty string,
21352            * which means the view / model values in `ngModel` and subsequently the scope value
21353            * will also be an empty string.
21354            *
21355            *
21356            * @param {string} ngModel Assignable angular expression to data-bind to.
21357            * @param {string=} name Property name of the form under which the control is published.
21358            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21359            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21360            * @param {string=} required Sets `required` validation error key if the value is not entered.
21361            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21362            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21363            *    `required` when you want to data-bind to the `required` attribute.
21364            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21365            *    minlength.
21366            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21367            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21368            *    any length.
21369            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21370            *    that contains the regular expression body that will be converted to a regular expression
21371            *    as in the ngPattern directive.
21372            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21373            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21374            *    If the expression evaluates to a RegExp object, then this is used directly.
21375            *    If the expression evaluates to a string, then it will be converted to a RegExp
21376            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21377            *    `new RegExp('^abc$')`.<br />
21378            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21379            *    start at the index of the last search's match, thus not taking the whole input value into
21380            *    account.
21381            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21382            *    interaction with the input element.
21383            *
21384            * @example
21385               <example name="number-input-directive" module="numberExample">
21386                 <file name="index.html">
21387                  <script>
21388                    angular.module('numberExample', [])
21389                      .controller('ExampleController', ['$scope', function($scope) {
21390                        $scope.example = {
21391                          value: 12
21392                        };
21393                      }]);
21394                  </script>
21395                  <form name="myForm" ng-controller="ExampleController">
21396                    <label>Number:
21397                      <input type="number" name="input" ng-model="example.value"
21398                             min="0" max="99" required>
21399                   </label>
21400                    <div role="alert">
21401                      <span class="error" ng-show="myForm.input.$error.required">
21402                        Required!</span>
21403                      <span class="error" ng-show="myForm.input.$error.number">
21404                        Not valid number!</span>
21405                    </div>
21406                    <tt>value = {{example.value}}</tt><br/>
21407                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21408                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21409                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21410                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21411                   </form>
21412                 </file>
21413                 <file name="protractor.js" type="protractor">
21414                   var value = element(by.binding('example.value'));
21415                   var valid = element(by.binding('myForm.input.$valid'));
21416                   var input = element(by.model('example.value'));
21417
21418                   it('should initialize to model', function() {
21419                     expect(value.getText()).toContain('12');
21420                     expect(valid.getText()).toContain('true');
21421                   });
21422
21423                   it('should be invalid if empty', function() {
21424                     input.clear();
21425                     input.sendKeys('');
21426                     expect(value.getText()).toEqual('value =');
21427                     expect(valid.getText()).toContain('false');
21428                   });
21429
21430                   it('should be invalid if over max', function() {
21431                     input.clear();
21432                     input.sendKeys('123');
21433                     expect(value.getText()).toEqual('value =');
21434                     expect(valid.getText()).toContain('false');
21435                   });
21436                 </file>
21437               </example>
21438            */
21439           'number': numberInputType,
21440
21441
21442           /**
21443            * @ngdoc input
21444            * @name input[url]
21445            *
21446            * @description
21447            * Text input with URL validation. Sets the `url` validation error key if the content is not a
21448            * valid URL.
21449            *
21450            * <div class="alert alert-warning">
21451            * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21452            * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21453            * the built-in validators (see the {@link guide/forms Forms guide})
21454            * </div>
21455            *
21456            * @param {string} ngModel Assignable angular expression to data-bind to.
21457            * @param {string=} name Property name of the form under which the control is published.
21458            * @param {string=} required Sets `required` validation error key if the value is not entered.
21459            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21460            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21461            *    `required` when you want to data-bind to the `required` attribute.
21462            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21463            *    minlength.
21464            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21465            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21466            *    any length.
21467            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21468            *    that contains the regular expression body that will be converted to a regular expression
21469            *    as in the ngPattern directive.
21470            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21471            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21472            *    If the expression evaluates to a RegExp object, then this is used directly.
21473            *    If the expression evaluates to a string, then it will be converted to a RegExp
21474            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21475            *    `new RegExp('^abc$')`.<br />
21476            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21477            *    start at the index of the last search's match, thus not taking the whole input value into
21478            *    account.
21479            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21480            *    interaction with the input element.
21481            *
21482            * @example
21483               <example name="url-input-directive" module="urlExample">
21484                 <file name="index.html">
21485                  <script>
21486                    angular.module('urlExample', [])
21487                      .controller('ExampleController', ['$scope', function($scope) {
21488                        $scope.url = {
21489                          text: 'http://google.com'
21490                        };
21491                      }]);
21492                  </script>
21493                  <form name="myForm" ng-controller="ExampleController">
21494                    <label>URL:
21495                      <input type="url" name="input" ng-model="url.text" required>
21496                    <label>
21497                    <div role="alert">
21498                      <span class="error" ng-show="myForm.input.$error.required">
21499                        Required!</span>
21500                      <span class="error" ng-show="myForm.input.$error.url">
21501                        Not valid url!</span>
21502                    </div>
21503                    <tt>text = {{url.text}}</tt><br/>
21504                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21505                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21506                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21507                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21508                    <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21509                   </form>
21510                 </file>
21511                 <file name="protractor.js" type="protractor">
21512                   var text = element(by.binding('url.text'));
21513                   var valid = element(by.binding('myForm.input.$valid'));
21514                   var input = element(by.model('url.text'));
21515
21516                   it('should initialize to model', function() {
21517                     expect(text.getText()).toContain('http://google.com');
21518                     expect(valid.getText()).toContain('true');
21519                   });
21520
21521                   it('should be invalid if empty', function() {
21522                     input.clear();
21523                     input.sendKeys('');
21524
21525                     expect(text.getText()).toEqual('text =');
21526                     expect(valid.getText()).toContain('false');
21527                   });
21528
21529                   it('should be invalid if not url', function() {
21530                     input.clear();
21531                     input.sendKeys('box');
21532
21533                     expect(valid.getText()).toContain('false');
21534                   });
21535                 </file>
21536               </example>
21537            */
21538           'url': urlInputType,
21539
21540
21541           /**
21542            * @ngdoc input
21543            * @name input[email]
21544            *
21545            * @description
21546            * Text input with email validation. Sets the `email` validation error key if not a valid email
21547            * address.
21548            *
21549            * <div class="alert alert-warning">
21550            * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21551            * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21552            * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21553            * </div>
21554            *
21555            * @param {string} ngModel Assignable angular expression to data-bind to.
21556            * @param {string=} name Property name of the form under which the control is published.
21557            * @param {string=} required Sets `required` validation error key if the value is not entered.
21558            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21559            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21560            *    `required` when you want to data-bind to the `required` attribute.
21561            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21562            *    minlength.
21563            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21564            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21565            *    any length.
21566            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21567            *    that contains the regular expression body that will be converted to a regular expression
21568            *    as in the ngPattern directive.
21569            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21570            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21571            *    If the expression evaluates to a RegExp object, then this is used directly.
21572            *    If the expression evaluates to a string, then it will be converted to a RegExp
21573            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21574            *    `new RegExp('^abc$')`.<br />
21575            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21576            *    start at the index of the last search's match, thus not taking the whole input value into
21577            *    account.
21578            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21579            *    interaction with the input element.
21580            *
21581            * @example
21582               <example name="email-input-directive" module="emailExample">
21583                 <file name="index.html">
21584                  <script>
21585                    angular.module('emailExample', [])
21586                      .controller('ExampleController', ['$scope', function($scope) {
21587                        $scope.email = {
21588                          text: 'me@example.com'
21589                        };
21590                      }]);
21591                  </script>
21592                    <form name="myForm" ng-controller="ExampleController">
21593                      <label>Email:
21594                        <input type="email" name="input" ng-model="email.text" required>
21595                      </label>
21596                      <div role="alert">
21597                        <span class="error" ng-show="myForm.input.$error.required">
21598                          Required!</span>
21599                        <span class="error" ng-show="myForm.input.$error.email">
21600                          Not valid email!</span>
21601                      </div>
21602                      <tt>text = {{email.text}}</tt><br/>
21603                      <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21604                      <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21605                      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21606                      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21607                      <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21608                    </form>
21609                  </file>
21610                 <file name="protractor.js" type="protractor">
21611                   var text = element(by.binding('email.text'));
21612                   var valid = element(by.binding('myForm.input.$valid'));
21613                   var input = element(by.model('email.text'));
21614
21615                   it('should initialize to model', function() {
21616                     expect(text.getText()).toContain('me@example.com');
21617                     expect(valid.getText()).toContain('true');
21618                   });
21619
21620                   it('should be invalid if empty', function() {
21621                     input.clear();
21622                     input.sendKeys('');
21623                     expect(text.getText()).toEqual('text =');
21624                     expect(valid.getText()).toContain('false');
21625                   });
21626
21627                   it('should be invalid if not email', function() {
21628                     input.clear();
21629                     input.sendKeys('xxx');
21630
21631                     expect(valid.getText()).toContain('false');
21632                   });
21633                 </file>
21634               </example>
21635            */
21636           'email': emailInputType,
21637
21638
21639           /**
21640            * @ngdoc input
21641            * @name input[radio]
21642            *
21643            * @description
21644            * HTML radio button.
21645            *
21646            * @param {string} ngModel Assignable angular expression to data-bind to.
21647            * @param {string} value The value to which the `ngModel` expression should be set when selected.
21648            *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21649            *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21650            * @param {string=} name Property name of the form under which the control is published.
21651            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21652            *    interaction with the input element.
21653            * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21654            *    is selected. Should be used instead of the `value` attribute if you need
21655            *    a non-string `ngModel` (`boolean`, `array`, ...).
21656            *
21657            * @example
21658               <example name="radio-input-directive" module="radioExample">
21659                 <file name="index.html">
21660                  <script>
21661                    angular.module('radioExample', [])
21662                      .controller('ExampleController', ['$scope', function($scope) {
21663                        $scope.color = {
21664                          name: 'blue'
21665                        };
21666                        $scope.specialValue = {
21667                          "id": "12345",
21668                          "value": "green"
21669                        };
21670                      }]);
21671                  </script>
21672                  <form name="myForm" ng-controller="ExampleController">
21673                    <label>
21674                      <input type="radio" ng-model="color.name" value="red">
21675                      Red
21676                    </label><br/>
21677                    <label>
21678                      <input type="radio" ng-model="color.name" ng-value="specialValue">
21679                      Green
21680                    </label><br/>
21681                    <label>
21682                      <input type="radio" ng-model="color.name" value="blue">
21683                      Blue
21684                    </label><br/>
21685                    <tt>color = {{color.name | json}}</tt><br/>
21686                   </form>
21687                   Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21688                 </file>
21689                 <file name="protractor.js" type="protractor">
21690                   it('should change state', function() {
21691                     var color = element(by.binding('color.name'));
21692
21693                     expect(color.getText()).toContain('blue');
21694
21695                     element.all(by.model('color.name')).get(0).click();
21696
21697                     expect(color.getText()).toContain('red');
21698                   });
21699                 </file>
21700               </example>
21701            */
21702           'radio': radioInputType,
21703
21704
21705           /**
21706            * @ngdoc input
21707            * @name input[checkbox]
21708            *
21709            * @description
21710            * HTML checkbox.
21711            *
21712            * @param {string} ngModel Assignable angular expression to data-bind to.
21713            * @param {string=} name Property name of the form under which the control is published.
21714            * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21715            * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21716            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21717            *    interaction with the input element.
21718            *
21719            * @example
21720               <example name="checkbox-input-directive" module="checkboxExample">
21721                 <file name="index.html">
21722                  <script>
21723                    angular.module('checkboxExample', [])
21724                      .controller('ExampleController', ['$scope', function($scope) {
21725                        $scope.checkboxModel = {
21726                         value1 : true,
21727                         value2 : 'YES'
21728                       };
21729                      }]);
21730                  </script>
21731                  <form name="myForm" ng-controller="ExampleController">
21732                    <label>Value1:
21733                      <input type="checkbox" ng-model="checkboxModel.value1">
21734                    </label><br/>
21735                    <label>Value2:
21736                      <input type="checkbox" ng-model="checkboxModel.value2"
21737                             ng-true-value="'YES'" ng-false-value="'NO'">
21738                     </label><br/>
21739                    <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21740                    <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21741                   </form>
21742                 </file>
21743                 <file name="protractor.js" type="protractor">
21744                   it('should change state', function() {
21745                     var value1 = element(by.binding('checkboxModel.value1'));
21746                     var value2 = element(by.binding('checkboxModel.value2'));
21747
21748                     expect(value1.getText()).toContain('true');
21749                     expect(value2.getText()).toContain('YES');
21750
21751                     element(by.model('checkboxModel.value1')).click();
21752                     element(by.model('checkboxModel.value2')).click();
21753
21754                     expect(value1.getText()).toContain('false');
21755                     expect(value2.getText()).toContain('NO');
21756                   });
21757                 </file>
21758               </example>
21759            */
21760           'checkbox': checkboxInputType,
21761
21762           'hidden': noop,
21763           'button': noop,
21764           'submit': noop,
21765           'reset': noop,
21766           'file': noop
21767         };
21768
21769         function stringBasedInputType(ctrl) {
21770           ctrl.$formatters.push(function(value) {
21771             return ctrl.$isEmpty(value) ? value : value.toString();
21772           });
21773         }
21774
21775         function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21776           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21777           stringBasedInputType(ctrl);
21778         }
21779
21780         function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21781           var type = lowercase(element[0].type);
21782
21783           // In composition mode, users are still inputing intermediate text buffer,
21784           // hold the listener until composition is done.
21785           // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21786           if (!$sniffer.android) {
21787             var composing = false;
21788
21789             element.on('compositionstart', function(data) {
21790               composing = true;
21791             });
21792
21793             element.on('compositionend', function() {
21794               composing = false;
21795               listener();
21796             });
21797           }
21798
21799           var listener = function(ev) {
21800             if (timeout) {
21801               $browser.defer.cancel(timeout);
21802               timeout = null;
21803             }
21804             if (composing) return;
21805             var value = element.val(),
21806                 event = ev && ev.type;
21807
21808             // By default we will trim the value
21809             // If the attribute ng-trim exists we will avoid trimming
21810             // If input type is 'password', the value is never trimmed
21811             if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21812               value = trim(value);
21813             }
21814
21815             // If a control is suffering from bad input (due to native validators), browsers discard its
21816             // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21817             // control's value is the same empty value twice in a row.
21818             if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21819               ctrl.$setViewValue(value, event);
21820             }
21821           };
21822
21823           // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21824           // input event on backspace, delete or cut
21825           if ($sniffer.hasEvent('input')) {
21826             element.on('input', listener);
21827           } else {
21828             var timeout;
21829
21830             var deferListener = function(ev, input, origValue) {
21831               if (!timeout) {
21832                 timeout = $browser.defer(function() {
21833                   timeout = null;
21834                   if (!input || input.value !== origValue) {
21835                     listener(ev);
21836                   }
21837                 });
21838               }
21839             };
21840
21841             element.on('keydown', function(event) {
21842               var key = event.keyCode;
21843
21844               // ignore
21845               //    command            modifiers                   arrows
21846               if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21847
21848               deferListener(event, this, this.value);
21849             });
21850
21851             // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21852             if ($sniffer.hasEvent('paste')) {
21853               element.on('paste cut', deferListener);
21854             }
21855           }
21856
21857           // if user paste into input using mouse on older browser
21858           // or form autocomplete on newer browser, we need "change" event to catch it
21859           element.on('change', listener);
21860
21861           ctrl.$render = function() {
21862             // Workaround for Firefox validation #12102.
21863             var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21864             if (element.val() !== value) {
21865               element.val(value);
21866             }
21867           };
21868         }
21869
21870         function weekParser(isoWeek, existingDate) {
21871           if (isDate(isoWeek)) {
21872             return isoWeek;
21873           }
21874
21875           if (isString(isoWeek)) {
21876             WEEK_REGEXP.lastIndex = 0;
21877             var parts = WEEK_REGEXP.exec(isoWeek);
21878             if (parts) {
21879               var year = +parts[1],
21880                   week = +parts[2],
21881                   hours = 0,
21882                   minutes = 0,
21883                   seconds = 0,
21884                   milliseconds = 0,
21885                   firstThurs = getFirstThursdayOfYear(year),
21886                   addDays = (week - 1) * 7;
21887
21888               if (existingDate) {
21889                 hours = existingDate.getHours();
21890                 minutes = existingDate.getMinutes();
21891                 seconds = existingDate.getSeconds();
21892                 milliseconds = existingDate.getMilliseconds();
21893               }
21894
21895               return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21896             }
21897           }
21898
21899           return NaN;
21900         }
21901
21902         function createDateParser(regexp, mapping) {
21903           return function(iso, date) {
21904             var parts, map;
21905
21906             if (isDate(iso)) {
21907               return iso;
21908             }
21909
21910             if (isString(iso)) {
21911               // When a date is JSON'ified to wraps itself inside of an extra
21912               // set of double quotes. This makes the date parsing code unable
21913               // to match the date string and parse it as a date.
21914               if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21915                 iso = iso.substring(1, iso.length - 1);
21916               }
21917               if (ISO_DATE_REGEXP.test(iso)) {
21918                 return new Date(iso);
21919               }
21920               regexp.lastIndex = 0;
21921               parts = regexp.exec(iso);
21922
21923               if (parts) {
21924                 parts.shift();
21925                 if (date) {
21926                   map = {
21927                     yyyy: date.getFullYear(),
21928                     MM: date.getMonth() + 1,
21929                     dd: date.getDate(),
21930                     HH: date.getHours(),
21931                     mm: date.getMinutes(),
21932                     ss: date.getSeconds(),
21933                     sss: date.getMilliseconds() / 1000
21934                   };
21935                 } else {
21936                   map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21937                 }
21938
21939                 forEach(parts, function(part, index) {
21940                   if (index < mapping.length) {
21941                     map[mapping[index]] = +part;
21942                   }
21943                 });
21944                 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21945               }
21946             }
21947
21948             return NaN;
21949           };
21950         }
21951
21952         function createDateInputType(type, regexp, parseDate, format) {
21953           return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21954             badInputChecker(scope, element, attr, ctrl);
21955             baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21956             var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21957             var previousDate;
21958
21959             ctrl.$$parserName = type;
21960             ctrl.$parsers.push(function(value) {
21961               if (ctrl.$isEmpty(value)) return null;
21962               if (regexp.test(value)) {
21963                 // Note: We cannot read ctrl.$modelValue, as there might be a different
21964                 // parser/formatter in the processing chain so that the model
21965                 // contains some different data format!
21966                 var parsedDate = parseDate(value, previousDate);
21967                 if (timezone) {
21968                   parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21969                 }
21970                 return parsedDate;
21971               }
21972               return undefined;
21973             });
21974
21975             ctrl.$formatters.push(function(value) {
21976               if (value && !isDate(value)) {
21977                 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21978               }
21979               if (isValidDate(value)) {
21980                 previousDate = value;
21981                 if (previousDate && timezone) {
21982                   previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21983                 }
21984                 return $filter('date')(value, format, timezone);
21985               } else {
21986                 previousDate = null;
21987                 return '';
21988               }
21989             });
21990
21991             if (isDefined(attr.min) || attr.ngMin) {
21992               var minVal;
21993               ctrl.$validators.min = function(value) {
21994                 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21995               };
21996               attr.$observe('min', function(val) {
21997                 minVal = parseObservedDateValue(val);
21998                 ctrl.$validate();
21999               });
22000             }
22001
22002             if (isDefined(attr.max) || attr.ngMax) {
22003               var maxVal;
22004               ctrl.$validators.max = function(value) {
22005                 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22006               };
22007               attr.$observe('max', function(val) {
22008                 maxVal = parseObservedDateValue(val);
22009                 ctrl.$validate();
22010               });
22011             }
22012
22013             function isValidDate(value) {
22014               // Invalid Date: getTime() returns NaN
22015               return value && !(value.getTime && value.getTime() !== value.getTime());
22016             }
22017
22018             function parseObservedDateValue(val) {
22019               return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22020             }
22021           };
22022         }
22023
22024         function badInputChecker(scope, element, attr, ctrl) {
22025           var node = element[0];
22026           var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22027           if (nativeValidation) {
22028             ctrl.$parsers.push(function(value) {
22029               var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22030               // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22031               // - also sets validity.badInput (should only be validity.typeMismatch).
22032               // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22033               // - can ignore this case as we can still read out the erroneous email...
22034               return validity.badInput && !validity.typeMismatch ? undefined : value;
22035             });
22036           }
22037         }
22038
22039         function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22040           badInputChecker(scope, element, attr, ctrl);
22041           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22042
22043           ctrl.$$parserName = 'number';
22044           ctrl.$parsers.push(function(value) {
22045             if (ctrl.$isEmpty(value))      return null;
22046             if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22047             return undefined;
22048           });
22049
22050           ctrl.$formatters.push(function(value) {
22051             if (!ctrl.$isEmpty(value)) {
22052               if (!isNumber(value)) {
22053                 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22054               }
22055               value = value.toString();
22056             }
22057             return value;
22058           });
22059
22060           if (isDefined(attr.min) || attr.ngMin) {
22061             var minVal;
22062             ctrl.$validators.min = function(value) {
22063               return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22064             };
22065
22066             attr.$observe('min', function(val) {
22067               if (isDefined(val) && !isNumber(val)) {
22068                 val = parseFloat(val, 10);
22069               }
22070               minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22071               // TODO(matsko): implement validateLater to reduce number of validations
22072               ctrl.$validate();
22073             });
22074           }
22075
22076           if (isDefined(attr.max) || attr.ngMax) {
22077             var maxVal;
22078             ctrl.$validators.max = function(value) {
22079               return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22080             };
22081
22082             attr.$observe('max', function(val) {
22083               if (isDefined(val) && !isNumber(val)) {
22084                 val = parseFloat(val, 10);
22085               }
22086               maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22087               // TODO(matsko): implement validateLater to reduce number of validations
22088               ctrl.$validate();
22089             });
22090           }
22091         }
22092
22093         function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22094           // Note: no badInputChecker here by purpose as `url` is only a validation
22095           // in browsers, i.e. we can always read out input.value even if it is not valid!
22096           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22097           stringBasedInputType(ctrl);
22098
22099           ctrl.$$parserName = 'url';
22100           ctrl.$validators.url = function(modelValue, viewValue) {
22101             var value = modelValue || viewValue;
22102             return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22103           };
22104         }
22105
22106         function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22107           // Note: no badInputChecker here by purpose as `url` is only a validation
22108           // in browsers, i.e. we can always read out input.value even if it is not valid!
22109           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22110           stringBasedInputType(ctrl);
22111
22112           ctrl.$$parserName = 'email';
22113           ctrl.$validators.email = function(modelValue, viewValue) {
22114             var value = modelValue || viewValue;
22115             return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22116           };
22117         }
22118
22119         function radioInputType(scope, element, attr, ctrl) {
22120           // make the name unique, if not defined
22121           if (isUndefined(attr.name)) {
22122             element.attr('name', nextUid());
22123           }
22124
22125           var listener = function(ev) {
22126             if (element[0].checked) {
22127               ctrl.$setViewValue(attr.value, ev && ev.type);
22128             }
22129           };
22130
22131           element.on('click', listener);
22132
22133           ctrl.$render = function() {
22134             var value = attr.value;
22135             element[0].checked = (value == ctrl.$viewValue);
22136           };
22137
22138           attr.$observe('value', ctrl.$render);
22139         }
22140
22141         function parseConstantExpr($parse, context, name, expression, fallback) {
22142           var parseFn;
22143           if (isDefined(expression)) {
22144             parseFn = $parse(expression);
22145             if (!parseFn.constant) {
22146               throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22147                                            '`{1}`.', name, expression);
22148             }
22149             return parseFn(context);
22150           }
22151           return fallback;
22152         }
22153
22154         function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22155           var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22156           var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22157
22158           var listener = function(ev) {
22159             ctrl.$setViewValue(element[0].checked, ev && ev.type);
22160           };
22161
22162           element.on('click', listener);
22163
22164           ctrl.$render = function() {
22165             element[0].checked = ctrl.$viewValue;
22166           };
22167
22168           // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22169           // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22170           // it to a boolean.
22171           ctrl.$isEmpty = function(value) {
22172             return value === false;
22173           };
22174
22175           ctrl.$formatters.push(function(value) {
22176             return equals(value, trueValue);
22177           });
22178
22179           ctrl.$parsers.push(function(value) {
22180             return value ? trueValue : falseValue;
22181           });
22182         }
22183
22184
22185         /**
22186          * @ngdoc directive
22187          * @name textarea
22188          * @restrict E
22189          *
22190          * @description
22191          * HTML textarea element control with angular data-binding. The data-binding and validation
22192          * properties of this element are exactly the same as those of the
22193          * {@link ng.directive:input input element}.
22194          *
22195          * @param {string} ngModel Assignable angular expression to data-bind to.
22196          * @param {string=} name Property name of the form under which the control is published.
22197          * @param {string=} required Sets `required` validation error key if the value is not entered.
22198          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22199          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22200          *    `required` when you want to data-bind to the `required` attribute.
22201          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22202          *    minlength.
22203          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22204          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22205          *    length.
22206          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22207          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22208          *    If the expression evaluates to a RegExp object, then this is used directly.
22209          *    If the expression evaluates to a string, then it will be converted to a RegExp
22210          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22211          *    `new RegExp('^abc$')`.<br />
22212          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22213          *    start at the index of the last search's match, thus not taking the whole input value into
22214          *    account.
22215          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22216          *    interaction with the input element.
22217          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22218          */
22219
22220
22221         /**
22222          * @ngdoc directive
22223          * @name input
22224          * @restrict E
22225          *
22226          * @description
22227          * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22228          * input state control, and validation.
22229          * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22230          *
22231          * <div class="alert alert-warning">
22232          * **Note:** Not every feature offered is available for all input types.
22233          * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22234          * </div>
22235          *
22236          * @param {string} ngModel Assignable angular expression to data-bind to.
22237          * @param {string=} name Property name of the form under which the control is published.
22238          * @param {string=} required Sets `required` validation error key if the value is not entered.
22239          * @param {boolean=} ngRequired Sets `required` attribute if set to true
22240          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22241          *    minlength.
22242          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22243          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22244          *    length.
22245          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22246          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22247          *    If the expression evaluates to a RegExp object, then this is used directly.
22248          *    If the expression evaluates to a string, then it will be converted to a RegExp
22249          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22250          *    `new RegExp('^abc$')`.<br />
22251          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22252          *    start at the index of the last search's match, thus not taking the whole input value into
22253          *    account.
22254          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22255          *    interaction with the input element.
22256          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22257          *    This parameter is ignored for input[type=password] controls, which will never trim the
22258          *    input.
22259          *
22260          * @example
22261             <example name="input-directive" module="inputExample">
22262               <file name="index.html">
22263                <script>
22264                   angular.module('inputExample', [])
22265                     .controller('ExampleController', ['$scope', function($scope) {
22266                       $scope.user = {name: 'guest', last: 'visitor'};
22267                     }]);
22268                </script>
22269                <div ng-controller="ExampleController">
22270                  <form name="myForm">
22271                    <label>
22272                       User name:
22273                       <input type="text" name="userName" ng-model="user.name" required>
22274                    </label>
22275                    <div role="alert">
22276                      <span class="error" ng-show="myForm.userName.$error.required">
22277                       Required!</span>
22278                    </div>
22279                    <label>
22280                       Last name:
22281                       <input type="text" name="lastName" ng-model="user.last"
22282                       ng-minlength="3" ng-maxlength="10">
22283                    </label>
22284                    <div role="alert">
22285                      <span class="error" ng-show="myForm.lastName.$error.minlength">
22286                        Too short!</span>
22287                      <span class="error" ng-show="myForm.lastName.$error.maxlength">
22288                        Too long!</span>
22289                    </div>
22290                  </form>
22291                  <hr>
22292                  <tt>user = {{user}}</tt><br/>
22293                  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22294                  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22295                  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22296                  <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22297                  <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22298                  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22299                  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22300                  <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22301                </div>
22302               </file>
22303               <file name="protractor.js" type="protractor">
22304                 var user = element(by.exactBinding('user'));
22305                 var userNameValid = element(by.binding('myForm.userName.$valid'));
22306                 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22307                 var lastNameError = element(by.binding('myForm.lastName.$error'));
22308                 var formValid = element(by.binding('myForm.$valid'));
22309                 var userNameInput = element(by.model('user.name'));
22310                 var userLastInput = element(by.model('user.last'));
22311
22312                 it('should initialize to model', function() {
22313                   expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22314                   expect(userNameValid.getText()).toContain('true');
22315                   expect(formValid.getText()).toContain('true');
22316                 });
22317
22318                 it('should be invalid if empty when required', function() {
22319                   userNameInput.clear();
22320                   userNameInput.sendKeys('');
22321
22322                   expect(user.getText()).toContain('{"last":"visitor"}');
22323                   expect(userNameValid.getText()).toContain('false');
22324                   expect(formValid.getText()).toContain('false');
22325                 });
22326
22327                 it('should be valid if empty when min length is set', function() {
22328                   userLastInput.clear();
22329                   userLastInput.sendKeys('');
22330
22331                   expect(user.getText()).toContain('{"name":"guest","last":""}');
22332                   expect(lastNameValid.getText()).toContain('true');
22333                   expect(formValid.getText()).toContain('true');
22334                 });
22335
22336                 it('should be invalid if less than required min length', function() {
22337                   userLastInput.clear();
22338                   userLastInput.sendKeys('xx');
22339
22340                   expect(user.getText()).toContain('{"name":"guest"}');
22341                   expect(lastNameValid.getText()).toContain('false');
22342                   expect(lastNameError.getText()).toContain('minlength');
22343                   expect(formValid.getText()).toContain('false');
22344                 });
22345
22346                 it('should be invalid if longer than max length', function() {
22347                   userLastInput.clear();
22348                   userLastInput.sendKeys('some ridiculously long name');
22349
22350                   expect(user.getText()).toContain('{"name":"guest"}');
22351                   expect(lastNameValid.getText()).toContain('false');
22352                   expect(lastNameError.getText()).toContain('maxlength');
22353                   expect(formValid.getText()).toContain('false');
22354                 });
22355               </file>
22356             </example>
22357          */
22358         var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22359             function($browser, $sniffer, $filter, $parse) {
22360           return {
22361             restrict: 'E',
22362             require: ['?ngModel'],
22363             link: {
22364               pre: function(scope, element, attr, ctrls) {
22365                 if (ctrls[0]) {
22366                   (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22367                                                                       $browser, $filter, $parse);
22368                 }
22369               }
22370             }
22371           };
22372         }];
22373
22374
22375
22376         var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22377         /**
22378          * @ngdoc directive
22379          * @name ngValue
22380          *
22381          * @description
22382          * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22383          * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22384          * the bound value.
22385          *
22386          * `ngValue` is useful when dynamically generating lists of radio buttons using
22387          * {@link ngRepeat `ngRepeat`}, as shown below.
22388          *
22389          * Likewise, `ngValue` can be used to generate `<option>` elements for
22390          * the {@link select `select`} element. In that case however, only strings are supported
22391          * for the `value `attribute, so the resulting `ngModel` will always be a string.
22392          * Support for `select` models with non-string values is available via `ngOptions`.
22393          *
22394          * @element input
22395          * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22396          *   of the `input` element
22397          *
22398          * @example
22399             <example name="ngValue-directive" module="valueExample">
22400               <file name="index.html">
22401                <script>
22402                   angular.module('valueExample', [])
22403                     .controller('ExampleController', ['$scope', function($scope) {
22404                       $scope.names = ['pizza', 'unicorns', 'robots'];
22405                       $scope.my = { favorite: 'unicorns' };
22406                     }]);
22407                </script>
22408                 <form ng-controller="ExampleController">
22409                   <h2>Which is your favorite?</h2>
22410                     <label ng-repeat="name in names" for="{{name}}">
22411                       {{name}}
22412                       <input type="radio"
22413                              ng-model="my.favorite"
22414                              ng-value="name"
22415                              id="{{name}}"
22416                              name="favorite">
22417                     </label>
22418                   <div>You chose {{my.favorite}}</div>
22419                 </form>
22420               </file>
22421               <file name="protractor.js" type="protractor">
22422                 var favorite = element(by.binding('my.favorite'));
22423
22424                 it('should initialize to model', function() {
22425                   expect(favorite.getText()).toContain('unicorns');
22426                 });
22427                 it('should bind the values to the inputs', function() {
22428                   element.all(by.model('my.favorite')).get(0).click();
22429                   expect(favorite.getText()).toContain('pizza');
22430                 });
22431               </file>
22432             </example>
22433          */
22434         var ngValueDirective = function() {
22435           return {
22436             restrict: 'A',
22437             priority: 100,
22438             compile: function(tpl, tplAttr) {
22439               if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22440                 return function ngValueConstantLink(scope, elm, attr) {
22441                   attr.$set('value', scope.$eval(attr.ngValue));
22442                 };
22443               } else {
22444                 return function ngValueLink(scope, elm, attr) {
22445                   scope.$watch(attr.ngValue, function valueWatchAction(value) {
22446                     attr.$set('value', value);
22447                   });
22448                 };
22449               }
22450             }
22451           };
22452         };
22453
22454         /**
22455          * @ngdoc directive
22456          * @name ngBind
22457          * @restrict AC
22458          *
22459          * @description
22460          * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22461          * with the value of a given expression, and to update the text content when the value of that
22462          * expression changes.
22463          *
22464          * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22465          * `{{ expression }}` which is similar but less verbose.
22466          *
22467          * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22468          * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22469          * element attribute, it makes the bindings invisible to the user while the page is loading.
22470          *
22471          * An alternative solution to this problem would be using the
22472          * {@link ng.directive:ngCloak ngCloak} directive.
22473          *
22474          *
22475          * @element ANY
22476          * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22477          *
22478          * @example
22479          * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22480            <example module="bindExample">
22481              <file name="index.html">
22482                <script>
22483                  angular.module('bindExample', [])
22484                    .controller('ExampleController', ['$scope', function($scope) {
22485                      $scope.name = 'Whirled';
22486                    }]);
22487                </script>
22488                <div ng-controller="ExampleController">
22489                  <label>Enter name: <input type="text" ng-model="name"></label><br>
22490                  Hello <span ng-bind="name"></span>!
22491                </div>
22492              </file>
22493              <file name="protractor.js" type="protractor">
22494                it('should check ng-bind', function() {
22495                  var nameInput = element(by.model('name'));
22496
22497                  expect(element(by.binding('name')).getText()).toBe('Whirled');
22498                  nameInput.clear();
22499                  nameInput.sendKeys('world');
22500                  expect(element(by.binding('name')).getText()).toBe('world');
22501                });
22502              </file>
22503            </example>
22504          */
22505         var ngBindDirective = ['$compile', function($compile) {
22506           return {
22507             restrict: 'AC',
22508             compile: function ngBindCompile(templateElement) {
22509               $compile.$$addBindingClass(templateElement);
22510               return function ngBindLink(scope, element, attr) {
22511                 $compile.$$addBindingInfo(element, attr.ngBind);
22512                 element = element[0];
22513                 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22514                   element.textContent = isUndefined(value) ? '' : value;
22515                 });
22516               };
22517             }
22518           };
22519         }];
22520
22521
22522         /**
22523          * @ngdoc directive
22524          * @name ngBindTemplate
22525          *
22526          * @description
22527          * The `ngBindTemplate` directive specifies that the element
22528          * text content should be replaced with the interpolation of the template
22529          * in the `ngBindTemplate` attribute.
22530          * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22531          * expressions. This directive is needed since some HTML elements
22532          * (such as TITLE and OPTION) cannot contain SPAN elements.
22533          *
22534          * @element ANY
22535          * @param {string} ngBindTemplate template of form
22536          *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22537          *
22538          * @example
22539          * Try it here: enter text in text box and watch the greeting change.
22540            <example module="bindExample">
22541              <file name="index.html">
22542                <script>
22543                  angular.module('bindExample', [])
22544                    .controller('ExampleController', ['$scope', function($scope) {
22545                      $scope.salutation = 'Hello';
22546                      $scope.name = 'World';
22547                    }]);
22548                </script>
22549                <div ng-controller="ExampleController">
22550                 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22551                 <label>Name: <input type="text" ng-model="name"></label><br>
22552                 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22553                </div>
22554              </file>
22555              <file name="protractor.js" type="protractor">
22556                it('should check ng-bind', function() {
22557                  var salutationElem = element(by.binding('salutation'));
22558                  var salutationInput = element(by.model('salutation'));
22559                  var nameInput = element(by.model('name'));
22560
22561                  expect(salutationElem.getText()).toBe('Hello World!');
22562
22563                  salutationInput.clear();
22564                  salutationInput.sendKeys('Greetings');
22565                  nameInput.clear();
22566                  nameInput.sendKeys('user');
22567
22568                  expect(salutationElem.getText()).toBe('Greetings user!');
22569                });
22570              </file>
22571            </example>
22572          */
22573         var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22574           return {
22575             compile: function ngBindTemplateCompile(templateElement) {
22576               $compile.$$addBindingClass(templateElement);
22577               return function ngBindTemplateLink(scope, element, attr) {
22578                 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22579                 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22580                 element = element[0];
22581                 attr.$observe('ngBindTemplate', function(value) {
22582                   element.textContent = isUndefined(value) ? '' : value;
22583                 });
22584               };
22585             }
22586           };
22587         }];
22588
22589
22590         /**
22591          * @ngdoc directive
22592          * @name ngBindHtml
22593          *
22594          * @description
22595          * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22596          * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22597          * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22598          * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22599          * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22600          *
22601          * You may also bypass sanitization for values you know are safe. To do so, bind to
22602          * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22603          * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22604          *
22605          * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22606          * will have an exception (instead of an exploit.)
22607          *
22608          * @element ANY
22609          * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22610          *
22611          * @example
22612
22613            <example module="bindHtmlExample" deps="angular-sanitize.js">
22614              <file name="index.html">
22615                <div ng-controller="ExampleController">
22616                 <p ng-bind-html="myHTML"></p>
22617                </div>
22618              </file>
22619
22620              <file name="script.js">
22621                angular.module('bindHtmlExample', ['ngSanitize'])
22622                  .controller('ExampleController', ['$scope', function($scope) {
22623                    $scope.myHTML =
22624                       'I am an <code>HTML</code>string with ' +
22625                       '<a href="#">links!</a> and other <em>stuff</em>';
22626                  }]);
22627              </file>
22628
22629              <file name="protractor.js" type="protractor">
22630                it('should check ng-bind-html', function() {
22631                  expect(element(by.binding('myHTML')).getText()).toBe(
22632                      'I am an HTMLstring with links! and other stuff');
22633                });
22634              </file>
22635            </example>
22636          */
22637         var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22638           return {
22639             restrict: 'A',
22640             compile: function ngBindHtmlCompile(tElement, tAttrs) {
22641               var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22642               var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22643                 return (value || '').toString();
22644               });
22645               $compile.$$addBindingClass(tElement);
22646
22647               return function ngBindHtmlLink(scope, element, attr) {
22648                 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22649
22650                 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22651                   // we re-evaluate the expr because we want a TrustedValueHolderType
22652                   // for $sce, not a string
22653                   element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22654                 });
22655               };
22656             }
22657           };
22658         }];
22659
22660         /**
22661          * @ngdoc directive
22662          * @name ngChange
22663          *
22664          * @description
22665          * Evaluate the given expression when the user changes the input.
22666          * The expression is evaluated immediately, unlike the JavaScript onchange event
22667          * which only triggers at the end of a change (usually, when the user leaves the
22668          * form element or presses the return key).
22669          *
22670          * The `ngChange` expression is only evaluated when a change in the input value causes
22671          * a new value to be committed to the model.
22672          *
22673          * It will not be evaluated:
22674          * * if the value returned from the `$parsers` transformation pipeline has not changed
22675          * * if the input has continued to be invalid since the model will stay `null`
22676          * * if the model is changed programmatically and not by a change to the input value
22677          *
22678          *
22679          * Note, this directive requires `ngModel` to be present.
22680          *
22681          * @element input
22682          * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22683          * in input value.
22684          *
22685          * @example
22686          * <example name="ngChange-directive" module="changeExample">
22687          *   <file name="index.html">
22688          *     <script>
22689          *       angular.module('changeExample', [])
22690          *         .controller('ExampleController', ['$scope', function($scope) {
22691          *           $scope.counter = 0;
22692          *           $scope.change = function() {
22693          *             $scope.counter++;
22694          *           };
22695          *         }]);
22696          *     </script>
22697          *     <div ng-controller="ExampleController">
22698          *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22699          *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22700          *       <label for="ng-change-example2">Confirmed</label><br />
22701          *       <tt>debug = {{confirmed}}</tt><br/>
22702          *       <tt>counter = {{counter}}</tt><br/>
22703          *     </div>
22704          *   </file>
22705          *   <file name="protractor.js" type="protractor">
22706          *     var counter = element(by.binding('counter'));
22707          *     var debug = element(by.binding('confirmed'));
22708          *
22709          *     it('should evaluate the expression if changing from view', function() {
22710          *       expect(counter.getText()).toContain('0');
22711          *
22712          *       element(by.id('ng-change-example1')).click();
22713          *
22714          *       expect(counter.getText()).toContain('1');
22715          *       expect(debug.getText()).toContain('true');
22716          *     });
22717          *
22718          *     it('should not evaluate the expression if changing from model', function() {
22719          *       element(by.id('ng-change-example2')).click();
22720
22721          *       expect(counter.getText()).toContain('0');
22722          *       expect(debug.getText()).toContain('true');
22723          *     });
22724          *   </file>
22725          * </example>
22726          */
22727         var ngChangeDirective = valueFn({
22728           restrict: 'A',
22729           require: 'ngModel',
22730           link: function(scope, element, attr, ctrl) {
22731             ctrl.$viewChangeListeners.push(function() {
22732               scope.$eval(attr.ngChange);
22733             });
22734           }
22735         });
22736
22737         function classDirective(name, selector) {
22738           name = 'ngClass' + name;
22739           return ['$animate', function($animate) {
22740             return {
22741               restrict: 'AC',
22742               link: function(scope, element, attr) {
22743                 var oldVal;
22744
22745                 scope.$watch(attr[name], ngClassWatchAction, true);
22746
22747                 attr.$observe('class', function(value) {
22748                   ngClassWatchAction(scope.$eval(attr[name]));
22749                 });
22750
22751
22752                 if (name !== 'ngClass') {
22753                   scope.$watch('$index', function($index, old$index) {
22754                     // jshint bitwise: false
22755                     var mod = $index & 1;
22756                     if (mod !== (old$index & 1)) {
22757                       var classes = arrayClasses(scope.$eval(attr[name]));
22758                       mod === selector ?
22759                         addClasses(classes) :
22760                         removeClasses(classes);
22761                     }
22762                   });
22763                 }
22764
22765                 function addClasses(classes) {
22766                   var newClasses = digestClassCounts(classes, 1);
22767                   attr.$addClass(newClasses);
22768                 }
22769
22770                 function removeClasses(classes) {
22771                   var newClasses = digestClassCounts(classes, -1);
22772                   attr.$removeClass(newClasses);
22773                 }
22774
22775                 function digestClassCounts(classes, count) {
22776                   // Use createMap() to prevent class assumptions involving property
22777                   // names in Object.prototype
22778                   var classCounts = element.data('$classCounts') || createMap();
22779                   var classesToUpdate = [];
22780                   forEach(classes, function(className) {
22781                     if (count > 0 || classCounts[className]) {
22782                       classCounts[className] = (classCounts[className] || 0) + count;
22783                       if (classCounts[className] === +(count > 0)) {
22784                         classesToUpdate.push(className);
22785                       }
22786                     }
22787                   });
22788                   element.data('$classCounts', classCounts);
22789                   return classesToUpdate.join(' ');
22790                 }
22791
22792                 function updateClasses(oldClasses, newClasses) {
22793                   var toAdd = arrayDifference(newClasses, oldClasses);
22794                   var toRemove = arrayDifference(oldClasses, newClasses);
22795                   toAdd = digestClassCounts(toAdd, 1);
22796                   toRemove = digestClassCounts(toRemove, -1);
22797                   if (toAdd && toAdd.length) {
22798                     $animate.addClass(element, toAdd);
22799                   }
22800                   if (toRemove && toRemove.length) {
22801                     $animate.removeClass(element, toRemove);
22802                   }
22803                 }
22804
22805                 function ngClassWatchAction(newVal) {
22806                   if (selector === true || scope.$index % 2 === selector) {
22807                     var newClasses = arrayClasses(newVal || []);
22808                     if (!oldVal) {
22809                       addClasses(newClasses);
22810                     } else if (!equals(newVal,oldVal)) {
22811                       var oldClasses = arrayClasses(oldVal);
22812                       updateClasses(oldClasses, newClasses);
22813                     }
22814                   }
22815                   oldVal = shallowCopy(newVal);
22816                 }
22817               }
22818             };
22819
22820             function arrayDifference(tokens1, tokens2) {
22821               var values = [];
22822
22823               outer:
22824               for (var i = 0; i < tokens1.length; i++) {
22825                 var token = tokens1[i];
22826                 for (var j = 0; j < tokens2.length; j++) {
22827                   if (token == tokens2[j]) continue outer;
22828                 }
22829                 values.push(token);
22830               }
22831               return values;
22832             }
22833
22834             function arrayClasses(classVal) {
22835               var classes = [];
22836               if (isArray(classVal)) {
22837                 forEach(classVal, function(v) {
22838                   classes = classes.concat(arrayClasses(v));
22839                 });
22840                 return classes;
22841               } else if (isString(classVal)) {
22842                 return classVal.split(' ');
22843               } else if (isObject(classVal)) {
22844                 forEach(classVal, function(v, k) {
22845                   if (v) {
22846                     classes = classes.concat(k.split(' '));
22847                   }
22848                 });
22849                 return classes;
22850               }
22851               return classVal;
22852             }
22853           }];
22854         }
22855
22856         /**
22857          * @ngdoc directive
22858          * @name ngClass
22859          * @restrict AC
22860          *
22861          * @description
22862          * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22863          * an expression that represents all classes to be added.
22864          *
22865          * The directive operates in three different ways, depending on which of three types the expression
22866          * evaluates to:
22867          *
22868          * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22869          * names.
22870          *
22871          * 2. If the expression evaluates to an object, then for each key-value pair of the
22872          * object with a truthy value the corresponding key is used as a class name.
22873          *
22874          * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22875          * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22876          * to give you more control over what CSS classes appear. See the code below for an example of this.
22877          *
22878          *
22879          * The directive won't add duplicate classes if a particular class was already set.
22880          *
22881          * When the expression changes, the previously added classes are removed and only then are the
22882          * new classes added.
22883          *
22884          * @animations
22885          * **add** - happens just before the class is applied to the elements
22886          *
22887          * **remove** - happens just before the class is removed from the element
22888          *
22889          * @element ANY
22890          * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22891          *   of the evaluation can be a string representing space delimited class
22892          *   names, an array, or a map of class names to boolean values. In the case of a map, the
22893          *   names of the properties whose values are truthy will be added as css classes to the
22894          *   element.
22895          *
22896          * @example Example that demonstrates basic bindings via ngClass directive.
22897            <example>
22898              <file name="index.html">
22899                <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22900                <label>
22901                   <input type="checkbox" ng-model="deleted">
22902                   deleted (apply "strike" class)
22903                </label><br>
22904                <label>
22905                   <input type="checkbox" ng-model="important">
22906                   important (apply "bold" class)
22907                </label><br>
22908                <label>
22909                   <input type="checkbox" ng-model="error">
22910                   error (apply "has-error" class)
22911                </label>
22912                <hr>
22913                <p ng-class="style">Using String Syntax</p>
22914                <input type="text" ng-model="style"
22915                       placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22916                <hr>
22917                <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22918                <input ng-model="style1"
22919                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22920                <input ng-model="style2"
22921                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22922                <input ng-model="style3"
22923                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22924                <hr>
22925                <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22926                <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22927                <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22928              </file>
22929              <file name="style.css">
22930                .strike {
22931                    text-decoration: line-through;
22932                }
22933                .bold {
22934                    font-weight: bold;
22935                }
22936                .red {
22937                    color: red;
22938                }
22939                .has-error {
22940                    color: red;
22941                    background-color: yellow;
22942                }
22943                .orange {
22944                    color: orange;
22945                }
22946              </file>
22947              <file name="protractor.js" type="protractor">
22948                var ps = element.all(by.css('p'));
22949
22950                it('should let you toggle the class', function() {
22951
22952                  expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22953                  expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22954
22955                  element(by.model('important')).click();
22956                  expect(ps.first().getAttribute('class')).toMatch(/bold/);
22957
22958                  element(by.model('error')).click();
22959                  expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22960                });
22961
22962                it('should let you toggle string example', function() {
22963                  expect(ps.get(1).getAttribute('class')).toBe('');
22964                  element(by.model('style')).clear();
22965                  element(by.model('style')).sendKeys('red');
22966                  expect(ps.get(1).getAttribute('class')).toBe('red');
22967                });
22968
22969                it('array example should have 3 classes', function() {
22970                  expect(ps.get(2).getAttribute('class')).toBe('');
22971                  element(by.model('style1')).sendKeys('bold');
22972                  element(by.model('style2')).sendKeys('strike');
22973                  element(by.model('style3')).sendKeys('red');
22974                  expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22975                });
22976
22977                it('array with map example should have 2 classes', function() {
22978                  expect(ps.last().getAttribute('class')).toBe('');
22979                  element(by.model('style4')).sendKeys('bold');
22980                  element(by.model('warning')).click();
22981                  expect(ps.last().getAttribute('class')).toBe('bold orange');
22982                });
22983              </file>
22984            </example>
22985
22986            ## Animations
22987
22988            The example below demonstrates how to perform animations using ngClass.
22989
22990            <example module="ngAnimate" deps="angular-animate.js" animations="true">
22991              <file name="index.html">
22992               <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22993               <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22994               <br>
22995               <span class="base-class" ng-class="myVar">Sample Text</span>
22996              </file>
22997              <file name="style.css">
22998                .base-class {
22999                  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23000                }
23001
23002                .base-class.my-class {
23003                  color: red;
23004                  font-size:3em;
23005                }
23006              </file>
23007              <file name="protractor.js" type="protractor">
23008                it('should check ng-class', function() {
23009                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23010                    toMatch(/my-class/);
23011
23012                  element(by.id('setbtn')).click();
23013
23014                  expect(element(by.css('.base-class')).getAttribute('class')).
23015                    toMatch(/my-class/);
23016
23017                  element(by.id('clearbtn')).click();
23018
23019                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23020                    toMatch(/my-class/);
23021                });
23022              </file>
23023            </example>
23024
23025
23026            ## ngClass and pre-existing CSS3 Transitions/Animations
23027            The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23028            Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23029            any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23030            to view the step by step details of {@link $animate#addClass $animate.addClass} and
23031            {@link $animate#removeClass $animate.removeClass}.
23032          */
23033         var ngClassDirective = classDirective('', true);
23034
23035         /**
23036          * @ngdoc directive
23037          * @name ngClassOdd
23038          * @restrict AC
23039          *
23040          * @description
23041          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23042          * {@link ng.directive:ngClass ngClass}, except they work in
23043          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23044          *
23045          * This directive can be applied only within the scope of an
23046          * {@link ng.directive:ngRepeat ngRepeat}.
23047          *
23048          * @element ANY
23049          * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23050          *   of the evaluation can be a string representing space delimited class names or an array.
23051          *
23052          * @example
23053            <example>
23054              <file name="index.html">
23055                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23056                   <li ng-repeat="name in names">
23057                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23058                      {{name}}
23059                    </span>
23060                   </li>
23061                 </ol>
23062              </file>
23063              <file name="style.css">
23064                .odd {
23065                  color: red;
23066                }
23067                .even {
23068                  color: blue;
23069                }
23070              </file>
23071              <file name="protractor.js" type="protractor">
23072                it('should check ng-class-odd and ng-class-even', function() {
23073                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23074                    toMatch(/odd/);
23075                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23076                    toMatch(/even/);
23077                });
23078              </file>
23079            </example>
23080          */
23081         var ngClassOddDirective = classDirective('Odd', 0);
23082
23083         /**
23084          * @ngdoc directive
23085          * @name ngClassEven
23086          * @restrict AC
23087          *
23088          * @description
23089          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23090          * {@link ng.directive:ngClass ngClass}, except they work in
23091          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23092          *
23093          * This directive can be applied only within the scope of an
23094          * {@link ng.directive:ngRepeat ngRepeat}.
23095          *
23096          * @element ANY
23097          * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23098          *   result of the evaluation can be a string representing space delimited class names or an array.
23099          *
23100          * @example
23101            <example>
23102              <file name="index.html">
23103                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23104                   <li ng-repeat="name in names">
23105                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23106                      {{name}} &nbsp; &nbsp; &nbsp;
23107                    </span>
23108                   </li>
23109                 </ol>
23110              </file>
23111              <file name="style.css">
23112                .odd {
23113                  color: red;
23114                }
23115                .even {
23116                  color: blue;
23117                }
23118              </file>
23119              <file name="protractor.js" type="protractor">
23120                it('should check ng-class-odd and ng-class-even', function() {
23121                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23122                    toMatch(/odd/);
23123                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23124                    toMatch(/even/);
23125                });
23126              </file>
23127            </example>
23128          */
23129         var ngClassEvenDirective = classDirective('Even', 1);
23130
23131         /**
23132          * @ngdoc directive
23133          * @name ngCloak
23134          * @restrict AC
23135          *
23136          * @description
23137          * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23138          * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23139          * directive to avoid the undesirable flicker effect caused by the html template display.
23140          *
23141          * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23142          * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23143          * of the browser view.
23144          *
23145          * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23146          * `angular.min.js`.
23147          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23148          *
23149          * ```css
23150          * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23151          *   display: none !important;
23152          * }
23153          * ```
23154          *
23155          * When this css rule is loaded by the browser, all html elements (including their children) that
23156          * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23157          * during the compilation of the template it deletes the `ngCloak` element attribute, making
23158          * the compiled element visible.
23159          *
23160          * For the best result, the `angular.js` script must be loaded in the head section of the html
23161          * document; alternatively, the css rule above must be included in the external stylesheet of the
23162          * application.
23163          *
23164          * @element ANY
23165          *
23166          * @example
23167            <example>
23168              <file name="index.html">
23169                 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23170                 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23171              </file>
23172              <file name="protractor.js" type="protractor">
23173                it('should remove the template directive and css class', function() {
23174                  expect($('#template1').getAttribute('ng-cloak')).
23175                    toBeNull();
23176                  expect($('#template2').getAttribute('ng-cloak')).
23177                    toBeNull();
23178                });
23179              </file>
23180            </example>
23181          *
23182          */
23183         var ngCloakDirective = ngDirective({
23184           compile: function(element, attr) {
23185             attr.$set('ngCloak', undefined);
23186             element.removeClass('ng-cloak');
23187           }
23188         });
23189
23190         /**
23191          * @ngdoc directive
23192          * @name ngController
23193          *
23194          * @description
23195          * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23196          * supports the principles behind the Model-View-Controller design pattern.
23197          *
23198          * MVC components in angular:
23199          *
23200          * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23201          *   are accessed through bindings.
23202          * * View — The template (HTML with data bindings) that is rendered into the View.
23203          * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23204          *   logic behind the application to decorate the scope with functions and values
23205          *
23206          * Note that you can also attach controllers to the DOM by declaring it in a route definition
23207          * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23208          * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23209          * and executed twice.
23210          *
23211          * @element ANY
23212          * @scope
23213          * @priority 500
23214          * @param {expression} ngController Name of a constructor function registered with the current
23215          * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23216          * that on the current scope evaluates to a constructor function.
23217          *
23218          * The controller instance can be published into a scope property by specifying
23219          * `ng-controller="as propertyName"`.
23220          *
23221          * If the current `$controllerProvider` is configured to use globals (via
23222          * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23223          * also be the name of a globally accessible constructor function (not recommended).
23224          *
23225          * @example
23226          * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23227          * greeting are methods declared on the controller (see source tab). These methods can
23228          * easily be called from the angular markup. Any changes to the data are automatically reflected
23229          * in the View without the need for a manual update.
23230          *
23231          * Two different declaration styles are included below:
23232          *
23233          * * one binds methods and properties directly onto the controller using `this`:
23234          * `ng-controller="SettingsController1 as settings"`
23235          * * one injects `$scope` into the controller:
23236          * `ng-controller="SettingsController2"`
23237          *
23238          * The second option is more common in the Angular community, and is generally used in boilerplates
23239          * and in this guide. However, there are advantages to binding properties directly to the controller
23240          * and avoiding scope.
23241          *
23242          * * Using `controller as` makes it obvious which controller you are accessing in the template when
23243          * multiple controllers apply to an element.
23244          * * If you are writing your controllers as classes you have easier access to the properties and
23245          * methods, which will appear on the scope, from inside the controller code.
23246          * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23247          * inheritance masking primitives.
23248          *
23249          * This example demonstrates the `controller as` syntax.
23250          *
23251          * <example name="ngControllerAs" module="controllerAsExample">
23252          *   <file name="index.html">
23253          *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23254          *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23255          *      <button ng-click="settings.greet()">greet</button><br/>
23256          *      Contact:
23257          *      <ul>
23258          *        <li ng-repeat="contact in settings.contacts">
23259          *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23260          *             <option>phone</option>
23261          *             <option>email</option>
23262          *          </select>
23263          *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23264          *          <button ng-click="settings.clearContact(contact)">clear</button>
23265          *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23266          *        </li>
23267          *        <li><button ng-click="settings.addContact()">add</button></li>
23268          *     </ul>
23269          *    </div>
23270          *   </file>
23271          *   <file name="app.js">
23272          *    angular.module('controllerAsExample', [])
23273          *      .controller('SettingsController1', SettingsController1);
23274          *
23275          *    function SettingsController1() {
23276          *      this.name = "John Smith";
23277          *      this.contacts = [
23278          *        {type: 'phone', value: '408 555 1212'},
23279          *        {type: 'email', value: 'john.smith@example.org'} ];
23280          *    }
23281          *
23282          *    SettingsController1.prototype.greet = function() {
23283          *      alert(this.name);
23284          *    };
23285          *
23286          *    SettingsController1.prototype.addContact = function() {
23287          *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
23288          *    };
23289          *
23290          *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23291          *     var index = this.contacts.indexOf(contactToRemove);
23292          *      this.contacts.splice(index, 1);
23293          *    };
23294          *
23295          *    SettingsController1.prototype.clearContact = function(contact) {
23296          *      contact.type = 'phone';
23297          *      contact.value = '';
23298          *    };
23299          *   </file>
23300          *   <file name="protractor.js" type="protractor">
23301          *     it('should check controller as', function() {
23302          *       var container = element(by.id('ctrl-as-exmpl'));
23303          *         expect(container.element(by.model('settings.name'))
23304          *           .getAttribute('value')).toBe('John Smith');
23305          *
23306          *       var firstRepeat =
23307          *           container.element(by.repeater('contact in settings.contacts').row(0));
23308          *       var secondRepeat =
23309          *           container.element(by.repeater('contact in settings.contacts').row(1));
23310          *
23311          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23312          *           .toBe('408 555 1212');
23313          *
23314          *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23315          *           .toBe('john.smith@example.org');
23316          *
23317          *       firstRepeat.element(by.buttonText('clear')).click();
23318          *
23319          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23320          *           .toBe('');
23321          *
23322          *       container.element(by.buttonText('add')).click();
23323          *
23324          *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23325          *           .element(by.model('contact.value'))
23326          *           .getAttribute('value'))
23327          *           .toBe('yourname@example.org');
23328          *     });
23329          *   </file>
23330          * </example>
23331          *
23332          * This example demonstrates the "attach to `$scope`" style of controller.
23333          *
23334          * <example name="ngController" module="controllerExample">
23335          *  <file name="index.html">
23336          *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23337          *     <label>Name: <input type="text" ng-model="name"/></label>
23338          *     <button ng-click="greet()">greet</button><br/>
23339          *     Contact:
23340          *     <ul>
23341          *       <li ng-repeat="contact in contacts">
23342          *         <select ng-model="contact.type" id="select_{{$index}}">
23343          *            <option>phone</option>
23344          *            <option>email</option>
23345          *         </select>
23346          *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23347          *         <button ng-click="clearContact(contact)">clear</button>
23348          *         <button ng-click="removeContact(contact)">X</button>
23349          *       </li>
23350          *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23351          *    </ul>
23352          *   </div>
23353          *  </file>
23354          *  <file name="app.js">
23355          *   angular.module('controllerExample', [])
23356          *     .controller('SettingsController2', ['$scope', SettingsController2]);
23357          *
23358          *   function SettingsController2($scope) {
23359          *     $scope.name = "John Smith";
23360          *     $scope.contacts = [
23361          *       {type:'phone', value:'408 555 1212'},
23362          *       {type:'email', value:'john.smith@example.org'} ];
23363          *
23364          *     $scope.greet = function() {
23365          *       alert($scope.name);
23366          *     };
23367          *
23368          *     $scope.addContact = function() {
23369          *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
23370          *     };
23371          *
23372          *     $scope.removeContact = function(contactToRemove) {
23373          *       var index = $scope.contacts.indexOf(contactToRemove);
23374          *       $scope.contacts.splice(index, 1);
23375          *     };
23376          *
23377          *     $scope.clearContact = function(contact) {
23378          *       contact.type = 'phone';
23379          *       contact.value = '';
23380          *     };
23381          *   }
23382          *  </file>
23383          *  <file name="protractor.js" type="protractor">
23384          *    it('should check controller', function() {
23385          *      var container = element(by.id('ctrl-exmpl'));
23386          *
23387          *      expect(container.element(by.model('name'))
23388          *          .getAttribute('value')).toBe('John Smith');
23389          *
23390          *      var firstRepeat =
23391          *          container.element(by.repeater('contact in contacts').row(0));
23392          *      var secondRepeat =
23393          *          container.element(by.repeater('contact in contacts').row(1));
23394          *
23395          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23396          *          .toBe('408 555 1212');
23397          *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23398          *          .toBe('john.smith@example.org');
23399          *
23400          *      firstRepeat.element(by.buttonText('clear')).click();
23401          *
23402          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23403          *          .toBe('');
23404          *
23405          *      container.element(by.buttonText('add')).click();
23406          *
23407          *      expect(container.element(by.repeater('contact in contacts').row(2))
23408          *          .element(by.model('contact.value'))
23409          *          .getAttribute('value'))
23410          *          .toBe('yourname@example.org');
23411          *    });
23412          *  </file>
23413          *</example>
23414
23415          */
23416         var ngControllerDirective = [function() {
23417           return {
23418             restrict: 'A',
23419             scope: true,
23420             controller: '@',
23421             priority: 500
23422           };
23423         }];
23424
23425         /**
23426          * @ngdoc directive
23427          * @name ngCsp
23428          *
23429          * @element html
23430          * @description
23431          *
23432          * Angular has some features that can break certain
23433          * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23434          *
23435          * If you intend to implement these rules then you must tell Angular not to use these features.
23436          *
23437          * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23438          *
23439          *
23440          * The following rules affect Angular:
23441          *
23442          * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23443          * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23444          * increase in the speed of evaluating Angular expressions.
23445          *
23446          * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23447          * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23448          * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23449          * `angular-csp.css` in your HTML manually.
23450          *
23451          * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23452          * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23453          * however, triggers a CSP error to be logged in the console:
23454          *
23455          * ```
23456          * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23457          * script in the following Content Security Policy directive: "default-src 'self'". Note that
23458          * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23459          * ```
23460          *
23461          * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23462          * directive on an element of the HTML document that appears before the `<script>` tag that loads
23463          * the `angular.js` file.
23464          *
23465          * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23466          *
23467          * You can specify which of the CSP related Angular features should be deactivated by providing
23468          * a value for the `ng-csp` attribute. The options are as follows:
23469          *
23470          * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23471          *
23472          * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23473          *
23474          * You can use these values in the following combinations:
23475          *
23476          *
23477          * * No declaration means that Angular will assume that you can do inline styles, but it will do
23478          * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23479          * of Angular.
23480          *
23481          * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23482          * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23483          * of Angular.
23484          *
23485          * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23486          * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23487          *
23488          * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23489          * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23490          *
23491          * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23492          * styles nor use eval, which is the same as an empty: ng-csp.
23493          * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23494          *
23495          * @example
23496          * This example shows how to apply the `ngCsp` directive to the `html` tag.
23497            ```html
23498              <!doctype html>
23499              <html ng-app ng-csp>
23500              ...
23501              ...
23502              </html>
23503            ```
23504           * @example
23505               // Note: the suffix `.csp` in the example name triggers
23506               // csp mode in our http server!
23507               <example name="example.csp" module="cspExample" ng-csp="true">
23508                 <file name="index.html">
23509                   <div ng-controller="MainController as ctrl">
23510                     <div>
23511                       <button ng-click="ctrl.inc()" id="inc">Increment</button>
23512                       <span id="counter">
23513                         {{ctrl.counter}}
23514                       </span>
23515                     </div>
23516
23517                     <div>
23518                       <button ng-click="ctrl.evil()" id="evil">Evil</button>
23519                       <span id="evilError">
23520                         {{ctrl.evilError}}
23521                       </span>
23522                     </div>
23523                   </div>
23524                 </file>
23525                 <file name="script.js">
23526                    angular.module('cspExample', [])
23527                      .controller('MainController', function() {
23528                         this.counter = 0;
23529                         this.inc = function() {
23530                           this.counter++;
23531                         };
23532                         this.evil = function() {
23533                           // jshint evil:true
23534                           try {
23535                             eval('1+2');
23536                           } catch (e) {
23537                             this.evilError = e.message;
23538                           }
23539                         };
23540                       });
23541                 </file>
23542                 <file name="protractor.js" type="protractor">
23543                   var util, webdriver;
23544
23545                   var incBtn = element(by.id('inc'));
23546                   var counter = element(by.id('counter'));
23547                   var evilBtn = element(by.id('evil'));
23548                   var evilError = element(by.id('evilError'));
23549
23550                   function getAndClearSevereErrors() {
23551                     return browser.manage().logs().get('browser').then(function(browserLog) {
23552                       return browserLog.filter(function(logEntry) {
23553                         return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23554                       });
23555                     });
23556                   }
23557
23558                   function clearErrors() {
23559                     getAndClearSevereErrors();
23560                   }
23561
23562                   function expectNoErrors() {
23563                     getAndClearSevereErrors().then(function(filteredLog) {
23564                       expect(filteredLog.length).toEqual(0);
23565                       if (filteredLog.length) {
23566                         console.log('browser console errors: ' + util.inspect(filteredLog));
23567                       }
23568                     });
23569                   }
23570
23571                   function expectError(regex) {
23572                     getAndClearSevereErrors().then(function(filteredLog) {
23573                       var found = false;
23574                       filteredLog.forEach(function(log) {
23575                         if (log.message.match(regex)) {
23576                           found = true;
23577                         }
23578                       });
23579                       if (!found) {
23580                         throw new Error('expected an error that matches ' + regex);
23581                       }
23582                     });
23583                   }
23584
23585                   beforeEach(function() {
23586                     util = require('util');
23587                     webdriver = require('protractor/node_modules/selenium-webdriver');
23588                   });
23589
23590                   // For now, we only test on Chrome,
23591                   // as Safari does not load the page with Protractor's injected scripts,
23592                   // and Firefox webdriver always disables content security policy (#6358)
23593                   if (browser.params.browser !== 'chrome') {
23594                     return;
23595                   }
23596
23597                   it('should not report errors when the page is loaded', function() {
23598                     // clear errors so we are not dependent on previous tests
23599                     clearErrors();
23600                     // Need to reload the page as the page is already loaded when
23601                     // we come here
23602                     browser.driver.getCurrentUrl().then(function(url) {
23603                       browser.get(url);
23604                     });
23605                     expectNoErrors();
23606                   });
23607
23608                   it('should evaluate expressions', function() {
23609                     expect(counter.getText()).toEqual('0');
23610                     incBtn.click();
23611                     expect(counter.getText()).toEqual('1');
23612                     expectNoErrors();
23613                   });
23614
23615                   it('should throw and report an error when using "eval"', function() {
23616                     evilBtn.click();
23617                     expect(evilError.getText()).toMatch(/Content Security Policy/);
23618                     expectError(/Content Security Policy/);
23619                   });
23620                 </file>
23621               </example>
23622           */
23623
23624         // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23625         // bootstrap the system (before $parse is instantiated), for this reason we just have
23626         // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23627
23628         /**
23629          * @ngdoc directive
23630          * @name ngClick
23631          *
23632          * @description
23633          * The ngClick directive allows you to specify custom behavior when
23634          * an element is clicked.
23635          *
23636          * @element ANY
23637          * @priority 0
23638          * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23639          * click. ({@link guide/expression#-event- Event object is available as `$event`})
23640          *
23641          * @example
23642            <example>
23643              <file name="index.html">
23644               <button ng-click="count = count + 1" ng-init="count=0">
23645                 Increment
23646               </button>
23647               <span>
23648                 count: {{count}}
23649               </span>
23650              </file>
23651              <file name="protractor.js" type="protractor">
23652                it('should check ng-click', function() {
23653                  expect(element(by.binding('count')).getText()).toMatch('0');
23654                  element(by.css('button')).click();
23655                  expect(element(by.binding('count')).getText()).toMatch('1');
23656                });
23657              </file>
23658            </example>
23659          */
23660         /*
23661          * A collection of directives that allows creation of custom event handlers that are defined as
23662          * angular expressions and are compiled and executed within the current scope.
23663          */
23664         var ngEventDirectives = {};
23665
23666         // For events that might fire synchronously during DOM manipulation
23667         // we need to execute their event handlers asynchronously using $evalAsync,
23668         // so that they are not executed in an inconsistent state.
23669         var forceAsyncEvents = {
23670           'blur': true,
23671           'focus': true
23672         };
23673         forEach(
23674           'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23675           function(eventName) {
23676             var directiveName = directiveNormalize('ng-' + eventName);
23677             ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23678               return {
23679                 restrict: 'A',
23680                 compile: function($element, attr) {
23681                   // We expose the powerful $event object on the scope that provides access to the Window,
23682                   // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23683                   // checks at the cost of speed since event handler expressions are not executed as
23684                   // frequently as regular change detection.
23685                   var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23686                   return function ngEventHandler(scope, element) {
23687                     element.on(eventName, function(event) {
23688                       var callback = function() {
23689                         fn(scope, {$event:event});
23690                       };
23691                       if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23692                         scope.$evalAsync(callback);
23693                       } else {
23694                         scope.$apply(callback);
23695                       }
23696                     });
23697                   };
23698                 }
23699               };
23700             }];
23701           }
23702         );
23703
23704         /**
23705          * @ngdoc directive
23706          * @name ngDblclick
23707          *
23708          * @description
23709          * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23710          *
23711          * @element ANY
23712          * @priority 0
23713          * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23714          * a dblclick. (The Event object is available as `$event`)
23715          *
23716          * @example
23717            <example>
23718              <file name="index.html">
23719               <button ng-dblclick="count = count + 1" ng-init="count=0">
23720                 Increment (on double click)
23721               </button>
23722               count: {{count}}
23723              </file>
23724            </example>
23725          */
23726
23727
23728         /**
23729          * @ngdoc directive
23730          * @name ngMousedown
23731          *
23732          * @description
23733          * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23734          *
23735          * @element ANY
23736          * @priority 0
23737          * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23738          * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23739          *
23740          * @example
23741            <example>
23742              <file name="index.html">
23743               <button ng-mousedown="count = count + 1" ng-init="count=0">
23744                 Increment (on mouse down)
23745               </button>
23746               count: {{count}}
23747              </file>
23748            </example>
23749          */
23750
23751
23752         /**
23753          * @ngdoc directive
23754          * @name ngMouseup
23755          *
23756          * @description
23757          * Specify custom behavior on mouseup event.
23758          *
23759          * @element ANY
23760          * @priority 0
23761          * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23762          * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23763          *
23764          * @example
23765            <example>
23766              <file name="index.html">
23767               <button ng-mouseup="count = count + 1" ng-init="count=0">
23768                 Increment (on mouse up)
23769               </button>
23770               count: {{count}}
23771              </file>
23772            </example>
23773          */
23774
23775         /**
23776          * @ngdoc directive
23777          * @name ngMouseover
23778          *
23779          * @description
23780          * Specify custom behavior on mouseover event.
23781          *
23782          * @element ANY
23783          * @priority 0
23784          * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23785          * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23786          *
23787          * @example
23788            <example>
23789              <file name="index.html">
23790               <button ng-mouseover="count = count + 1" ng-init="count=0">
23791                 Increment (when mouse is over)
23792               </button>
23793               count: {{count}}
23794              </file>
23795            </example>
23796          */
23797
23798
23799         /**
23800          * @ngdoc directive
23801          * @name ngMouseenter
23802          *
23803          * @description
23804          * Specify custom behavior on mouseenter event.
23805          *
23806          * @element ANY
23807          * @priority 0
23808          * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23809          * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23810          *
23811          * @example
23812            <example>
23813              <file name="index.html">
23814               <button ng-mouseenter="count = count + 1" ng-init="count=0">
23815                 Increment (when mouse enters)
23816               </button>
23817               count: {{count}}
23818              </file>
23819            </example>
23820          */
23821
23822
23823         /**
23824          * @ngdoc directive
23825          * @name ngMouseleave
23826          *
23827          * @description
23828          * Specify custom behavior on mouseleave event.
23829          *
23830          * @element ANY
23831          * @priority 0
23832          * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23833          * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23834          *
23835          * @example
23836            <example>
23837              <file name="index.html">
23838               <button ng-mouseleave="count = count + 1" ng-init="count=0">
23839                 Increment (when mouse leaves)
23840               </button>
23841               count: {{count}}
23842              </file>
23843            </example>
23844          */
23845
23846
23847         /**
23848          * @ngdoc directive
23849          * @name ngMousemove
23850          *
23851          * @description
23852          * Specify custom behavior on mousemove event.
23853          *
23854          * @element ANY
23855          * @priority 0
23856          * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23857          * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23858          *
23859          * @example
23860            <example>
23861              <file name="index.html">
23862               <button ng-mousemove="count = count + 1" ng-init="count=0">
23863                 Increment (when mouse moves)
23864               </button>
23865               count: {{count}}
23866              </file>
23867            </example>
23868          */
23869
23870
23871         /**
23872          * @ngdoc directive
23873          * @name ngKeydown
23874          *
23875          * @description
23876          * Specify custom behavior on keydown event.
23877          *
23878          * @element ANY
23879          * @priority 0
23880          * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23881          * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23882          *
23883          * @example
23884            <example>
23885              <file name="index.html">
23886               <input ng-keydown="count = count + 1" ng-init="count=0">
23887               key down count: {{count}}
23888              </file>
23889            </example>
23890          */
23891
23892
23893         /**
23894          * @ngdoc directive
23895          * @name ngKeyup
23896          *
23897          * @description
23898          * Specify custom behavior on keyup event.
23899          *
23900          * @element ANY
23901          * @priority 0
23902          * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23903          * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23904          *
23905          * @example
23906            <example>
23907              <file name="index.html">
23908                <p>Typing in the input box below updates the key count</p>
23909                <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23910
23911                <p>Typing in the input box below updates the keycode</p>
23912                <input ng-keyup="event=$event">
23913                <p>event keyCode: {{ event.keyCode }}</p>
23914                <p>event altKey: {{ event.altKey }}</p>
23915              </file>
23916            </example>
23917          */
23918
23919
23920         /**
23921          * @ngdoc directive
23922          * @name ngKeypress
23923          *
23924          * @description
23925          * Specify custom behavior on keypress event.
23926          *
23927          * @element ANY
23928          * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23929          * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23930          * and can be interrogated for keyCode, altKey, etc.)
23931          *
23932          * @example
23933            <example>
23934              <file name="index.html">
23935               <input ng-keypress="count = count + 1" ng-init="count=0">
23936               key press count: {{count}}
23937              </file>
23938            </example>
23939          */
23940
23941
23942         /**
23943          * @ngdoc directive
23944          * @name ngSubmit
23945          *
23946          * @description
23947          * Enables binding angular expressions to onsubmit events.
23948          *
23949          * Additionally it prevents the default action (which for form means sending the request to the
23950          * server and reloading the current page), but only if the form does not contain `action`,
23951          * `data-action`, or `x-action` attributes.
23952          *
23953          * <div class="alert alert-warning">
23954          * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23955          * `ngSubmit` handlers together. See the
23956          * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23957          * for a detailed discussion of when `ngSubmit` may be triggered.
23958          * </div>
23959          *
23960          * @element form
23961          * @priority 0
23962          * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23963          * ({@link guide/expression#-event- Event object is available as `$event`})
23964          *
23965          * @example
23966            <example module="submitExample">
23967              <file name="index.html">
23968               <script>
23969                 angular.module('submitExample', [])
23970                   .controller('ExampleController', ['$scope', function($scope) {
23971                     $scope.list = [];
23972                     $scope.text = 'hello';
23973                     $scope.submit = function() {
23974                       if ($scope.text) {
23975                         $scope.list.push(this.text);
23976                         $scope.text = '';
23977                       }
23978                     };
23979                   }]);
23980               </script>
23981               <form ng-submit="submit()" ng-controller="ExampleController">
23982                 Enter text and hit enter:
23983                 <input type="text" ng-model="text" name="text" />
23984                 <input type="submit" id="submit" value="Submit" />
23985                 <pre>list={{list}}</pre>
23986               </form>
23987              </file>
23988              <file name="protractor.js" type="protractor">
23989                it('should check ng-submit', function() {
23990                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23991                  element(by.css('#submit')).click();
23992                  expect(element(by.binding('list')).getText()).toContain('hello');
23993                  expect(element(by.model('text')).getAttribute('value')).toBe('');
23994                });
23995                it('should ignore empty strings', function() {
23996                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23997                  element(by.css('#submit')).click();
23998                  element(by.css('#submit')).click();
23999                  expect(element(by.binding('list')).getText()).toContain('hello');
24000                 });
24001              </file>
24002            </example>
24003          */
24004
24005         /**
24006          * @ngdoc directive
24007          * @name ngFocus
24008          *
24009          * @description
24010          * Specify custom behavior on focus event.
24011          *
24012          * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24013          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24014          * during an `$apply` to ensure a consistent state.
24015          *
24016          * @element window, input, select, textarea, a
24017          * @priority 0
24018          * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24019          * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24020          *
24021          * @example
24022          * See {@link ng.directive:ngClick ngClick}
24023          */
24024
24025         /**
24026          * @ngdoc directive
24027          * @name ngBlur
24028          *
24029          * @description
24030          * Specify custom behavior on blur event.
24031          *
24032          * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24033          * an element has lost focus.
24034          *
24035          * Note: As the `blur` event is executed synchronously also during DOM manipulations
24036          * (e.g. removing a focussed input),
24037          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24038          * during an `$apply` to ensure a consistent state.
24039          *
24040          * @element window, input, select, textarea, a
24041          * @priority 0
24042          * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24043          * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24044          *
24045          * @example
24046          * See {@link ng.directive:ngClick ngClick}
24047          */
24048
24049         /**
24050          * @ngdoc directive
24051          * @name ngCopy
24052          *
24053          * @description
24054          * Specify custom behavior on copy event.
24055          *
24056          * @element window, input, select, textarea, a
24057          * @priority 0
24058          * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24059          * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24060          *
24061          * @example
24062            <example>
24063              <file name="index.html">
24064               <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24065               copied: {{copied}}
24066              </file>
24067            </example>
24068          */
24069
24070         /**
24071          * @ngdoc directive
24072          * @name ngCut
24073          *
24074          * @description
24075          * Specify custom behavior on cut event.
24076          *
24077          * @element window, input, select, textarea, a
24078          * @priority 0
24079          * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24080          * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24081          *
24082          * @example
24083            <example>
24084              <file name="index.html">
24085               <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24086               cut: {{cut}}
24087              </file>
24088            </example>
24089          */
24090
24091         /**
24092          * @ngdoc directive
24093          * @name ngPaste
24094          *
24095          * @description
24096          * Specify custom behavior on paste event.
24097          *
24098          * @element window, input, select, textarea, a
24099          * @priority 0
24100          * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24101          * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24102          *
24103          * @example
24104            <example>
24105              <file name="index.html">
24106               <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24107               pasted: {{paste}}
24108              </file>
24109            </example>
24110          */
24111
24112         /**
24113          * @ngdoc directive
24114          * @name ngIf
24115          * @restrict A
24116          * @multiElement
24117          *
24118          * @description
24119          * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24120          * {expression}. If the expression assigned to `ngIf` evaluates to a false
24121          * value then the element is removed from the DOM, otherwise a clone of the
24122          * element is reinserted into the DOM.
24123          *
24124          * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24125          * element in the DOM rather than changing its visibility via the `display` css property.  A common
24126          * case when this difference is significant is when using css selectors that rely on an element's
24127          * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24128          *
24129          * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24130          * is created when the element is restored.  The scope created within `ngIf` inherits from
24131          * its parent scope using
24132          * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24133          * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24134          * a javascript primitive defined in the parent scope. In this case any modifications made to the
24135          * variable within the child scope will override (hide) the value in the parent scope.
24136          *
24137          * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24138          * is if an element's class attribute is directly modified after it's compiled, using something like
24139          * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24140          * the added class will be lost because the original compiled state is used to regenerate the element.
24141          *
24142          * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24143          * and `leave` effects.
24144          *
24145          * @animations
24146          * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24147          * leave - happens just before the `ngIf` contents are removed from the DOM
24148          *
24149          * @element ANY
24150          * @scope
24151          * @priority 600
24152          * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24153          *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24154          *     element is added to the DOM tree.
24155          *
24156          * @example
24157           <example module="ngAnimate" deps="angular-animate.js" animations="true">
24158             <file name="index.html">
24159               <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24160               Show when checked:
24161               <span ng-if="checked" class="animate-if">
24162                 This is removed when the checkbox is unchecked.
24163               </span>
24164             </file>
24165             <file name="animations.css">
24166               .animate-if {
24167                 background:white;
24168                 border:1px solid black;
24169                 padding:10px;
24170               }
24171
24172               .animate-if.ng-enter, .animate-if.ng-leave {
24173                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24174               }
24175
24176               .animate-if.ng-enter,
24177               .animate-if.ng-leave.ng-leave-active {
24178                 opacity:0;
24179               }
24180
24181               .animate-if.ng-leave,
24182               .animate-if.ng-enter.ng-enter-active {
24183                 opacity:1;
24184               }
24185             </file>
24186           </example>
24187          */
24188         var ngIfDirective = ['$animate', function($animate) {
24189           return {
24190             multiElement: true,
24191             transclude: 'element',
24192             priority: 600,
24193             terminal: true,
24194             restrict: 'A',
24195             $$tlb: true,
24196             link: function($scope, $element, $attr, ctrl, $transclude) {
24197                 var block, childScope, previousElements;
24198                 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24199
24200                   if (value) {
24201                     if (!childScope) {
24202                       $transclude(function(clone, newScope) {
24203                         childScope = newScope;
24204                         clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24205                         // Note: We only need the first/last node of the cloned nodes.
24206                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24207                         // by a directive with templateUrl when its template arrives.
24208                         block = {
24209                           clone: clone
24210                         };
24211                         $animate.enter(clone, $element.parent(), $element);
24212                       });
24213                     }
24214                   } else {
24215                     if (previousElements) {
24216                       previousElements.remove();
24217                       previousElements = null;
24218                     }
24219                     if (childScope) {
24220                       childScope.$destroy();
24221                       childScope = null;
24222                     }
24223                     if (block) {
24224                       previousElements = getBlockNodes(block.clone);
24225                       $animate.leave(previousElements).then(function() {
24226                         previousElements = null;
24227                       });
24228                       block = null;
24229                     }
24230                   }
24231                 });
24232             }
24233           };
24234         }];
24235
24236         /**
24237          * @ngdoc directive
24238          * @name ngInclude
24239          * @restrict ECA
24240          *
24241          * @description
24242          * Fetches, compiles and includes an external HTML fragment.
24243          *
24244          * By default, the template URL is restricted to the same domain and protocol as the
24245          * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24246          * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24247          * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24248          * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24249          * ng.$sce Strict Contextual Escaping}.
24250          *
24251          * In addition, the browser's
24252          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24253          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24254          * policy may further restrict whether the template is successfully loaded.
24255          * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24256          * access on some browsers.
24257          *
24258          * @animations
24259          * enter - animation is used to bring new content into the browser.
24260          * leave - animation is used to animate existing content away.
24261          *
24262          * The enter and leave animation occur concurrently.
24263          *
24264          * @scope
24265          * @priority 400
24266          *
24267          * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24268          *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24269          * @param {string=} onload Expression to evaluate when a new partial is loaded.
24270          *                  <div class="alert alert-warning">
24271          *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24272          *                  a function with the name on the window element, which will usually throw a
24273          *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24274          *                  different form that {@link guide/directive#normalization matches} `onload`.
24275          *                  </div>
24276            *
24277          * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24278          *                  $anchorScroll} to scroll the viewport after the content is loaded.
24279          *
24280          *                  - If the attribute is not set, disable scrolling.
24281          *                  - If the attribute is set without value, enable scrolling.
24282          *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24283          *
24284          * @example
24285           <example module="includeExample" deps="angular-animate.js" animations="true">
24286             <file name="index.html">
24287              <div ng-controller="ExampleController">
24288                <select ng-model="template" ng-options="t.name for t in templates">
24289                 <option value="">(blank)</option>
24290                </select>
24291                url of the template: <code>{{template.url}}</code>
24292                <hr/>
24293                <div class="slide-animate-container">
24294                  <div class="slide-animate" ng-include="template.url"></div>
24295                </div>
24296              </div>
24297             </file>
24298             <file name="script.js">
24299               angular.module('includeExample', ['ngAnimate'])
24300                 .controller('ExampleController', ['$scope', function($scope) {
24301                   $scope.templates =
24302                     [ { name: 'template1.html', url: 'template1.html'},
24303                       { name: 'template2.html', url: 'template2.html'} ];
24304                   $scope.template = $scope.templates[0];
24305                 }]);
24306              </file>
24307             <file name="template1.html">
24308               Content of template1.html
24309             </file>
24310             <file name="template2.html">
24311               Content of template2.html
24312             </file>
24313             <file name="animations.css">
24314               .slide-animate-container {
24315                 position:relative;
24316                 background:white;
24317                 border:1px solid black;
24318                 height:40px;
24319                 overflow:hidden;
24320               }
24321
24322               .slide-animate {
24323                 padding:10px;
24324               }
24325
24326               .slide-animate.ng-enter, .slide-animate.ng-leave {
24327                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24328
24329                 position:absolute;
24330                 top:0;
24331                 left:0;
24332                 right:0;
24333                 bottom:0;
24334                 display:block;
24335                 padding:10px;
24336               }
24337
24338               .slide-animate.ng-enter {
24339                 top:-50px;
24340               }
24341               .slide-animate.ng-enter.ng-enter-active {
24342                 top:0;
24343               }
24344
24345               .slide-animate.ng-leave {
24346                 top:0;
24347               }
24348               .slide-animate.ng-leave.ng-leave-active {
24349                 top:50px;
24350               }
24351             </file>
24352             <file name="protractor.js" type="protractor">
24353               var templateSelect = element(by.model('template'));
24354               var includeElem = element(by.css('[ng-include]'));
24355
24356               it('should load template1.html', function() {
24357                 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24358               });
24359
24360               it('should load template2.html', function() {
24361                 if (browser.params.browser == 'firefox') {
24362                   // Firefox can't handle using selects
24363                   // See https://github.com/angular/protractor/issues/480
24364                   return;
24365                 }
24366                 templateSelect.click();
24367                 templateSelect.all(by.css('option')).get(2).click();
24368                 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24369               });
24370
24371               it('should change to blank', function() {
24372                 if (browser.params.browser == 'firefox') {
24373                   // Firefox can't handle using selects
24374                   return;
24375                 }
24376                 templateSelect.click();
24377                 templateSelect.all(by.css('option')).get(0).click();
24378                 expect(includeElem.isPresent()).toBe(false);
24379               });
24380             </file>
24381           </example>
24382          */
24383
24384
24385         /**
24386          * @ngdoc event
24387          * @name ngInclude#$includeContentRequested
24388          * @eventType emit on the scope ngInclude was declared in
24389          * @description
24390          * Emitted every time the ngInclude content is requested.
24391          *
24392          * @param {Object} angularEvent Synthetic event object.
24393          * @param {String} src URL of content to load.
24394          */
24395
24396
24397         /**
24398          * @ngdoc event
24399          * @name ngInclude#$includeContentLoaded
24400          * @eventType emit on the current ngInclude scope
24401          * @description
24402          * Emitted every time the ngInclude content is reloaded.
24403          *
24404          * @param {Object} angularEvent Synthetic event object.
24405          * @param {String} src URL of content to load.
24406          */
24407
24408
24409         /**
24410          * @ngdoc event
24411          * @name ngInclude#$includeContentError
24412          * @eventType emit on the scope ngInclude was declared in
24413          * @description
24414          * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24415          *
24416          * @param {Object} angularEvent Synthetic event object.
24417          * @param {String} src URL of content to load.
24418          */
24419         var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24420                           function($templateRequest,   $anchorScroll,   $animate) {
24421           return {
24422             restrict: 'ECA',
24423             priority: 400,
24424             terminal: true,
24425             transclude: 'element',
24426             controller: angular.noop,
24427             compile: function(element, attr) {
24428               var srcExp = attr.ngInclude || attr.src,
24429                   onloadExp = attr.onload || '',
24430                   autoScrollExp = attr.autoscroll;
24431
24432               return function(scope, $element, $attr, ctrl, $transclude) {
24433                 var changeCounter = 0,
24434                     currentScope,
24435                     previousElement,
24436                     currentElement;
24437
24438                 var cleanupLastIncludeContent = function() {
24439                   if (previousElement) {
24440                     previousElement.remove();
24441                     previousElement = null;
24442                   }
24443                   if (currentScope) {
24444                     currentScope.$destroy();
24445                     currentScope = null;
24446                   }
24447                   if (currentElement) {
24448                     $animate.leave(currentElement).then(function() {
24449                       previousElement = null;
24450                     });
24451                     previousElement = currentElement;
24452                     currentElement = null;
24453                   }
24454                 };
24455
24456                 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24457                   var afterAnimation = function() {
24458                     if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24459                       $anchorScroll();
24460                     }
24461                   };
24462                   var thisChangeId = ++changeCounter;
24463
24464                   if (src) {
24465                     //set the 2nd param to true to ignore the template request error so that the inner
24466                     //contents and scope can be cleaned up.
24467                     $templateRequest(src, true).then(function(response) {
24468                       if (thisChangeId !== changeCounter) return;
24469                       var newScope = scope.$new();
24470                       ctrl.template = response;
24471
24472                       // Note: This will also link all children of ng-include that were contained in the original
24473                       // html. If that content contains controllers, ... they could pollute/change the scope.
24474                       // However, using ng-include on an element with additional content does not make sense...
24475                       // Note: We can't remove them in the cloneAttchFn of $transclude as that
24476                       // function is called before linking the content, which would apply child
24477                       // directives to non existing elements.
24478                       var clone = $transclude(newScope, function(clone) {
24479                         cleanupLastIncludeContent();
24480                         $animate.enter(clone, null, $element).then(afterAnimation);
24481                       });
24482
24483                       currentScope = newScope;
24484                       currentElement = clone;
24485
24486                       currentScope.$emit('$includeContentLoaded', src);
24487                       scope.$eval(onloadExp);
24488                     }, function() {
24489                       if (thisChangeId === changeCounter) {
24490                         cleanupLastIncludeContent();
24491                         scope.$emit('$includeContentError', src);
24492                       }
24493                     });
24494                     scope.$emit('$includeContentRequested', src);
24495                   } else {
24496                     cleanupLastIncludeContent();
24497                     ctrl.template = null;
24498                   }
24499                 });
24500               };
24501             }
24502           };
24503         }];
24504
24505         // This directive is called during the $transclude call of the first `ngInclude` directive.
24506         // It will replace and compile the content of the element with the loaded template.
24507         // We need this directive so that the element content is already filled when
24508         // the link function of another directive on the same element as ngInclude
24509         // is called.
24510         var ngIncludeFillContentDirective = ['$compile',
24511           function($compile) {
24512             return {
24513               restrict: 'ECA',
24514               priority: -400,
24515               require: 'ngInclude',
24516               link: function(scope, $element, $attr, ctrl) {
24517                 if (/SVG/.test($element[0].toString())) {
24518                   // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24519                   // support innerHTML, so detect this here and try to generate the contents
24520                   // specially.
24521                   $element.empty();
24522                   $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24523                       function namespaceAdaptedClone(clone) {
24524                     $element.append(clone);
24525                   }, {futureParentElement: $element});
24526                   return;
24527                 }
24528
24529                 $element.html(ctrl.template);
24530                 $compile($element.contents())(scope);
24531               }
24532             };
24533           }];
24534
24535         /**
24536          * @ngdoc directive
24537          * @name ngInit
24538          * @restrict AC
24539          *
24540          * @description
24541          * The `ngInit` directive allows you to evaluate an expression in the
24542          * current scope.
24543          *
24544          * <div class="alert alert-danger">
24545          * This directive can be abused to add unnecessary amounts of logic into your templates.
24546          * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24547          * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24548          * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24549          * rather than `ngInit` to initialize values on a scope.
24550          * </div>
24551          *
24552          * <div class="alert alert-warning">
24553          * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24554          * sure you have parentheses to ensure correct operator precedence:
24555          * <pre class="prettyprint">
24556          * `<div ng-init="test1 = ($index | toString)"></div>`
24557          * </pre>
24558          * </div>
24559          *
24560          * @priority 450
24561          *
24562          * @element ANY
24563          * @param {expression} ngInit {@link guide/expression Expression} to eval.
24564          *
24565          * @example
24566            <example module="initExample">
24567              <file name="index.html">
24568            <script>
24569              angular.module('initExample', [])
24570                .controller('ExampleController', ['$scope', function($scope) {
24571                  $scope.list = [['a', 'b'], ['c', 'd']];
24572                }]);
24573            </script>
24574            <div ng-controller="ExampleController">
24575              <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24576                <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24577                   <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24578                </div>
24579              </div>
24580            </div>
24581              </file>
24582              <file name="protractor.js" type="protractor">
24583                it('should alias index positions', function() {
24584                  var elements = element.all(by.css('.example-init'));
24585                  expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24586                  expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24587                  expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24588                  expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24589                });
24590              </file>
24591            </example>
24592          */
24593         var ngInitDirective = ngDirective({
24594           priority: 450,
24595           compile: function() {
24596             return {
24597               pre: function(scope, element, attrs) {
24598                 scope.$eval(attrs.ngInit);
24599               }
24600             };
24601           }
24602         });
24603
24604         /**
24605          * @ngdoc directive
24606          * @name ngList
24607          *
24608          * @description
24609          * Text input that converts between a delimited string and an array of strings. The default
24610          * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24611          * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24612          *
24613          * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24614          * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24615          *   list item is respected. This implies that the user of the directive is responsible for
24616          *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24617          *   tab or newline character.
24618          * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24619          *   when joining the list items back together) and whitespace around each list item is stripped
24620          *   before it is added to the model.
24621          *
24622          * ### Example with Validation
24623          *
24624          * <example name="ngList-directive" module="listExample">
24625          *   <file name="app.js">
24626          *      angular.module('listExample', [])
24627          *        .controller('ExampleController', ['$scope', function($scope) {
24628          *          $scope.names = ['morpheus', 'neo', 'trinity'];
24629          *        }]);
24630          *   </file>
24631          *   <file name="index.html">
24632          *    <form name="myForm" ng-controller="ExampleController">
24633          *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24634          *      <span role="alert">
24635          *        <span class="error" ng-show="myForm.namesInput.$error.required">
24636          *        Required!</span>
24637          *      </span>
24638          *      <br>
24639          *      <tt>names = {{names}}</tt><br/>
24640          *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24641          *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24642          *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24643          *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24644          *     </form>
24645          *   </file>
24646          *   <file name="protractor.js" type="protractor">
24647          *     var listInput = element(by.model('names'));
24648          *     var names = element(by.exactBinding('names'));
24649          *     var valid = element(by.binding('myForm.namesInput.$valid'));
24650          *     var error = element(by.css('span.error'));
24651          *
24652          *     it('should initialize to model', function() {
24653          *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24654          *       expect(valid.getText()).toContain('true');
24655          *       expect(error.getCssValue('display')).toBe('none');
24656          *     });
24657          *
24658          *     it('should be invalid if empty', function() {
24659          *       listInput.clear();
24660          *       listInput.sendKeys('');
24661          *
24662          *       expect(names.getText()).toContain('');
24663          *       expect(valid.getText()).toContain('false');
24664          *       expect(error.getCssValue('display')).not.toBe('none');
24665          *     });
24666          *   </file>
24667          * </example>
24668          *
24669          * ### Example - splitting on newline
24670          * <example name="ngList-directive-newlines">
24671          *   <file name="index.html">
24672          *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24673          *    <pre>{{ list | json }}</pre>
24674          *   </file>
24675          *   <file name="protractor.js" type="protractor">
24676          *     it("should split the text by newlines", function() {
24677          *       var listInput = element(by.model('list'));
24678          *       var output = element(by.binding('list | json'));
24679          *       listInput.sendKeys('abc\ndef\nghi');
24680          *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24681          *     });
24682          *   </file>
24683          * </example>
24684          *
24685          * @element input
24686          * @param {string=} ngList optional delimiter that should be used to split the value.
24687          */
24688         var ngListDirective = function() {
24689           return {
24690             restrict: 'A',
24691             priority: 100,
24692             require: 'ngModel',
24693             link: function(scope, element, attr, ctrl) {
24694               // We want to control whitespace trimming so we use this convoluted approach
24695               // to access the ngList attribute, which doesn't pre-trim the attribute
24696               var ngList = element.attr(attr.$attr.ngList) || ', ';
24697               var trimValues = attr.ngTrim !== 'false';
24698               var separator = trimValues ? trim(ngList) : ngList;
24699
24700               var parse = function(viewValue) {
24701                 // If the viewValue is invalid (say required but empty) it will be `undefined`
24702                 if (isUndefined(viewValue)) return;
24703
24704                 var list = [];
24705
24706                 if (viewValue) {
24707                   forEach(viewValue.split(separator), function(value) {
24708                     if (value) list.push(trimValues ? trim(value) : value);
24709                   });
24710                 }
24711
24712                 return list;
24713               };
24714
24715               ctrl.$parsers.push(parse);
24716               ctrl.$formatters.push(function(value) {
24717                 if (isArray(value)) {
24718                   return value.join(ngList);
24719                 }
24720
24721                 return undefined;
24722               });
24723
24724               // Override the standard $isEmpty because an empty array means the input is empty.
24725               ctrl.$isEmpty = function(value) {
24726                 return !value || !value.length;
24727               };
24728             }
24729           };
24730         };
24731
24732         /* global VALID_CLASS: true,
24733           INVALID_CLASS: true,
24734           PRISTINE_CLASS: true,
24735           DIRTY_CLASS: true,
24736           UNTOUCHED_CLASS: true,
24737           TOUCHED_CLASS: true,
24738         */
24739
24740         var VALID_CLASS = 'ng-valid',
24741             INVALID_CLASS = 'ng-invalid',
24742             PRISTINE_CLASS = 'ng-pristine',
24743             DIRTY_CLASS = 'ng-dirty',
24744             UNTOUCHED_CLASS = 'ng-untouched',
24745             TOUCHED_CLASS = 'ng-touched',
24746             PENDING_CLASS = 'ng-pending';
24747
24748         var ngModelMinErr = minErr('ngModel');
24749
24750         /**
24751          * @ngdoc type
24752          * @name ngModel.NgModelController
24753          *
24754          * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24755          * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24756          * is set.
24757          * @property {*} $modelValue The value in the model that the control is bound to.
24758          * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24759                the control reads value from the DOM. The functions are called in array order, each passing
24760                its return value through to the next. The last return value is forwarded to the
24761                {@link ngModel.NgModelController#$validators `$validators`} collection.
24762
24763         Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24764         `$viewValue`}.
24765
24766         Returning `undefined` from a parser means a parse error occurred. In that case,
24767         no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24768         will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24769         is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24770
24771          *
24772          * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24773                the model value changes. The functions are called in reverse array order, each passing the value through to the
24774                next. The last return value is used as the actual DOM value.
24775                Used to format / convert values for display in the control.
24776          * ```js
24777          * function formatter(value) {
24778          *   if (value) {
24779          *     return value.toUpperCase();
24780          *   }
24781          * }
24782          * ngModel.$formatters.push(formatter);
24783          * ```
24784          *
24785          * @property {Object.<string, function>} $validators A collection of validators that are applied
24786          *      whenever the model value changes. The key value within the object refers to the name of the
24787          *      validator while the function refers to the validation operation. The validation operation is
24788          *      provided with the model value as an argument and must return a true or false value depending
24789          *      on the response of that validation.
24790          *
24791          * ```js
24792          * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24793          *   var value = modelValue || viewValue;
24794          *   return /[0-9]+/.test(value) &&
24795          *          /[a-z]+/.test(value) &&
24796          *          /[A-Z]+/.test(value) &&
24797          *          /\W+/.test(value);
24798          * };
24799          * ```
24800          *
24801          * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24802          *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24803          *      is expected to return a promise when it is run during the model validation process. Once the promise
24804          *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24805          *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24806          *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24807          *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24808          *      will only run once all synchronous validators have passed.
24809          *
24810          * Please note that if $http is used then it is important that the server returns a success HTTP response code
24811          * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24812          *
24813          * ```js
24814          * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24815          *   var value = modelValue || viewValue;
24816          *
24817          *   // Lookup user by username
24818          *   return $http.get('/api/users/' + value).
24819          *      then(function resolved() {
24820          *        //username exists, this means validation fails
24821          *        return $q.reject('exists');
24822          *      }, function rejected() {
24823          *        //username does not exist, therefore this validation passes
24824          *        return true;
24825          *      });
24826          * };
24827          * ```
24828          *
24829          * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24830          *     view value has changed. It is called with no arguments, and its return value is ignored.
24831          *     This can be used in place of additional $watches against the model value.
24832          *
24833          * @property {Object} $error An object hash with all failing validator ids as keys.
24834          * @property {Object} $pending An object hash with all pending validator ids as keys.
24835          *
24836          * @property {boolean} $untouched True if control has not lost focus yet.
24837          * @property {boolean} $touched True if control has lost focus.
24838          * @property {boolean} $pristine True if user has not interacted with the control yet.
24839          * @property {boolean} $dirty True if user has already interacted with the control.
24840          * @property {boolean} $valid True if there is no error.
24841          * @property {boolean} $invalid True if at least one error on the control.
24842          * @property {string} $name The name attribute of the control.
24843          *
24844          * @description
24845          *
24846          * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24847          * The controller contains services for data-binding, validation, CSS updates, and value formatting
24848          * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24849          * listening to DOM events.
24850          * Such DOM related logic should be provided by other directives which make use of
24851          * `NgModelController` for data-binding to control elements.
24852          * Angular provides this DOM logic for most {@link input `input`} elements.
24853          * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24854          * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24855          *
24856          * @example
24857          * ### Custom Control Example
24858          * This example shows how to use `NgModelController` with a custom control to achieve
24859          * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24860          * collaborate together to achieve the desired result.
24861          *
24862          * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24863          * contents be edited in place by the user.
24864          *
24865          * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24866          * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24867          * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24868          * that content using the `$sce` service.
24869          *
24870          * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24871             <file name="style.css">
24872               [contenteditable] {
24873                 border: 1px solid black;
24874                 background-color: white;
24875                 min-height: 20px;
24876               }
24877
24878               .ng-invalid {
24879                 border: 1px solid red;
24880               }
24881
24882             </file>
24883             <file name="script.js">
24884               angular.module('customControl', ['ngSanitize']).
24885                 directive('contenteditable', ['$sce', function($sce) {
24886                   return {
24887                     restrict: 'A', // only activate on element attribute
24888                     require: '?ngModel', // get a hold of NgModelController
24889                     link: function(scope, element, attrs, ngModel) {
24890                       if (!ngModel) return; // do nothing if no ng-model
24891
24892                       // Specify how UI should be updated
24893                       ngModel.$render = function() {
24894                         element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24895                       };
24896
24897                       // Listen for change events to enable binding
24898                       element.on('blur keyup change', function() {
24899                         scope.$evalAsync(read);
24900                       });
24901                       read(); // initialize
24902
24903                       // Write data to the model
24904                       function read() {
24905                         var html = element.html();
24906                         // When we clear the content editable the browser leaves a <br> behind
24907                         // If strip-br attribute is provided then we strip this out
24908                         if ( attrs.stripBr && html == '<br>' ) {
24909                           html = '';
24910                         }
24911                         ngModel.$setViewValue(html);
24912                       }
24913                     }
24914                   };
24915                 }]);
24916             </file>
24917             <file name="index.html">
24918               <form name="myForm">
24919                <div contenteditable
24920                     name="myWidget" ng-model="userContent"
24921                     strip-br="true"
24922                     required>Change me!</div>
24923                 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24924                <hr>
24925                <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24926               </form>
24927             </file>
24928             <file name="protractor.js" type="protractor">
24929             it('should data-bind and become invalid', function() {
24930               if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24931                 // SafariDriver can't handle contenteditable
24932                 // and Firefox driver can't clear contenteditables very well
24933                 return;
24934               }
24935               var contentEditable = element(by.css('[contenteditable]'));
24936               var content = 'Change me!';
24937
24938               expect(contentEditable.getText()).toEqual(content);
24939
24940               contentEditable.clear();
24941               contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24942               expect(contentEditable.getText()).toEqual('');
24943               expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24944             });
24945             </file>
24946          * </example>
24947          *
24948          *
24949          */
24950         var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24951             function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24952           this.$viewValue = Number.NaN;
24953           this.$modelValue = Number.NaN;
24954           this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24955           this.$validators = {};
24956           this.$asyncValidators = {};
24957           this.$parsers = [];
24958           this.$formatters = [];
24959           this.$viewChangeListeners = [];
24960           this.$untouched = true;
24961           this.$touched = false;
24962           this.$pristine = true;
24963           this.$dirty = false;
24964           this.$valid = true;
24965           this.$invalid = false;
24966           this.$error = {}; // keep invalid keys here
24967           this.$$success = {}; // keep valid keys here
24968           this.$pending = undefined; // keep pending keys here
24969           this.$name = $interpolate($attr.name || '', false)($scope);
24970           this.$$parentForm = nullFormCtrl;
24971
24972           var parsedNgModel = $parse($attr.ngModel),
24973               parsedNgModelAssign = parsedNgModel.assign,
24974               ngModelGet = parsedNgModel,
24975               ngModelSet = parsedNgModelAssign,
24976               pendingDebounce = null,
24977               parserValid,
24978               ctrl = this;
24979
24980           this.$$setOptions = function(options) {
24981             ctrl.$options = options;
24982             if (options && options.getterSetter) {
24983               var invokeModelGetter = $parse($attr.ngModel + '()'),
24984                   invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24985
24986               ngModelGet = function($scope) {
24987                 var modelValue = parsedNgModel($scope);
24988                 if (isFunction(modelValue)) {
24989                   modelValue = invokeModelGetter($scope);
24990                 }
24991                 return modelValue;
24992               };
24993               ngModelSet = function($scope, newValue) {
24994                 if (isFunction(parsedNgModel($scope))) {
24995                   invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24996                 } else {
24997                   parsedNgModelAssign($scope, ctrl.$modelValue);
24998                 }
24999               };
25000             } else if (!parsedNgModel.assign) {
25001               throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25002                   $attr.ngModel, startingTag($element));
25003             }
25004           };
25005
25006           /**
25007            * @ngdoc method
25008            * @name ngModel.NgModelController#$render
25009            *
25010            * @description
25011            * Called when the view needs to be updated. It is expected that the user of the ng-model
25012            * directive will implement this method.
25013            *
25014            * The `$render()` method is invoked in the following situations:
25015            *
25016            * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25017            *   committed value then `$render()` is called to update the input control.
25018            * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25019            *   the `$viewValue` are different from last time.
25020            *
25021            * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25022            * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25023            * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25024            * invoked if you only change a property on the objects.
25025            */
25026           this.$render = noop;
25027
25028           /**
25029            * @ngdoc method
25030            * @name ngModel.NgModelController#$isEmpty
25031            *
25032            * @description
25033            * This is called when we need to determine if the value of an input is empty.
25034            *
25035            * For instance, the required directive does this to work out if the input has data or not.
25036            *
25037            * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25038            *
25039            * You can override this for input directives whose concept of being empty is different from the
25040            * default. The `checkboxInputType` directive does this because in its case a value of `false`
25041            * implies empty.
25042            *
25043            * @param {*} value The value of the input to check for emptiness.
25044            * @returns {boolean} True if `value` is "empty".
25045            */
25046           this.$isEmpty = function(value) {
25047             return isUndefined(value) || value === '' || value === null || value !== value;
25048           };
25049
25050           var currentValidationRunId = 0;
25051
25052           /**
25053            * @ngdoc method
25054            * @name ngModel.NgModelController#$setValidity
25055            *
25056            * @description
25057            * Change the validity state, and notify the form.
25058            *
25059            * This method can be called within $parsers/$formatters or a custom validation implementation.
25060            * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25061            * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25062            *
25063            * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25064            *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25065            *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25066            *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25067            *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25068            *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25069            * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25070            *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25071            *                          Skipped is used by Angular when validators do not run because of parse errors and
25072            *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25073            */
25074           addSetValidityMethod({
25075             ctrl: this,
25076             $element: $element,
25077             set: function(object, property) {
25078               object[property] = true;
25079             },
25080             unset: function(object, property) {
25081               delete object[property];
25082             },
25083             $animate: $animate
25084           });
25085
25086           /**
25087            * @ngdoc method
25088            * @name ngModel.NgModelController#$setPristine
25089            *
25090            * @description
25091            * Sets the control to its pristine state.
25092            *
25093            * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25094            * state (`ng-pristine` class). A model is considered to be pristine when the control
25095            * has not been changed from when first compiled.
25096            */
25097           this.$setPristine = function() {
25098             ctrl.$dirty = false;
25099             ctrl.$pristine = true;
25100             $animate.removeClass($element, DIRTY_CLASS);
25101             $animate.addClass($element, PRISTINE_CLASS);
25102           };
25103
25104           /**
25105            * @ngdoc method
25106            * @name ngModel.NgModelController#$setDirty
25107            *
25108            * @description
25109            * Sets the control to its dirty state.
25110            *
25111            * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25112            * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25113            * from when first compiled.
25114            */
25115           this.$setDirty = function() {
25116             ctrl.$dirty = true;
25117             ctrl.$pristine = false;
25118             $animate.removeClass($element, PRISTINE_CLASS);
25119             $animate.addClass($element, DIRTY_CLASS);
25120             ctrl.$$parentForm.$setDirty();
25121           };
25122
25123           /**
25124            * @ngdoc method
25125            * @name ngModel.NgModelController#$setUntouched
25126            *
25127            * @description
25128            * Sets the control to its untouched state.
25129            *
25130            * This method can be called to remove the `ng-touched` class and set the control to its
25131            * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25132            * by default, however this function can be used to restore that state if the model has
25133            * already been touched by the user.
25134            */
25135           this.$setUntouched = function() {
25136             ctrl.$touched = false;
25137             ctrl.$untouched = true;
25138             $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25139           };
25140
25141           /**
25142            * @ngdoc method
25143            * @name ngModel.NgModelController#$setTouched
25144            *
25145            * @description
25146            * Sets the control to its touched state.
25147            *
25148            * This method can be called to remove the `ng-untouched` class and set the control to its
25149            * touched state (`ng-touched` class). A model is considered to be touched when the user has
25150            * first focused the control element and then shifted focus away from the control (blur event).
25151            */
25152           this.$setTouched = function() {
25153             ctrl.$touched = true;
25154             ctrl.$untouched = false;
25155             $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25156           };
25157
25158           /**
25159            * @ngdoc method
25160            * @name ngModel.NgModelController#$rollbackViewValue
25161            *
25162            * @description
25163            * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25164            * which may be caused by a pending debounced event or because the input is waiting for a some
25165            * future event.
25166            *
25167            * If you have an input that uses `ng-model-options` to set up debounced events or events such
25168            * as blur you can have a situation where there is a period when the `$viewValue`
25169            * is out of synch with the ngModel's `$modelValue`.
25170            *
25171            * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25172            * programmatically before these debounced/future events have resolved/occurred, because Angular's
25173            * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25174            *
25175            * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25176            * input which may have such events pending. This is important in order to make sure that the
25177            * input field will be updated with the new model value and any pending operations are cancelled.
25178            *
25179            * <example name="ng-model-cancel-update" module="cancel-update-example">
25180            *   <file name="app.js">
25181            *     angular.module('cancel-update-example', [])
25182            *
25183            *     .controller('CancelUpdateController', ['$scope', function($scope) {
25184            *       $scope.resetWithCancel = function(e) {
25185            *         if (e.keyCode == 27) {
25186            *           $scope.myForm.myInput1.$rollbackViewValue();
25187            *           $scope.myValue = '';
25188            *         }
25189            *       };
25190            *       $scope.resetWithoutCancel = function(e) {
25191            *         if (e.keyCode == 27) {
25192            *           $scope.myValue = '';
25193            *         }
25194            *       };
25195            *     }]);
25196            *   </file>
25197            *   <file name="index.html">
25198            *     <div ng-controller="CancelUpdateController">
25199            *       <p>Try typing something in each input.  See that the model only updates when you
25200            *          blur off the input.
25201            *        </p>
25202            *        <p>Now see what happens if you start typing then press the Escape key</p>
25203            *
25204            *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25205            *         <p id="inputDescription1">With $rollbackViewValue()</p>
25206            *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25207            *                ng-keydown="resetWithCancel($event)"><br/>
25208            *         myValue: "{{ myValue }}"
25209            *
25210            *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25211            *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25212            *                ng-keydown="resetWithoutCancel($event)"><br/>
25213            *         myValue: "{{ myValue }}"
25214            *       </form>
25215            *     </div>
25216            *   </file>
25217            * </example>
25218            */
25219           this.$rollbackViewValue = function() {
25220             $timeout.cancel(pendingDebounce);
25221             ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25222             ctrl.$render();
25223           };
25224
25225           /**
25226            * @ngdoc method
25227            * @name ngModel.NgModelController#$validate
25228            *
25229            * @description
25230            * Runs each of the registered validators (first synchronous validators and then
25231            * asynchronous validators).
25232            * If the validity changes to invalid, the model will be set to `undefined`,
25233            * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25234            * If the validity changes to valid, it will set the model to the last available valid
25235            * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25236            */
25237           this.$validate = function() {
25238             // ignore $validate before model is initialized
25239             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25240               return;
25241             }
25242
25243             var viewValue = ctrl.$$lastCommittedViewValue;
25244             // Note: we use the $$rawModelValue as $modelValue might have been
25245             // set to undefined during a view -> model update that found validation
25246             // errors. We can't parse the view here, since that could change
25247             // the model although neither viewValue nor the model on the scope changed
25248             var modelValue = ctrl.$$rawModelValue;
25249
25250             var prevValid = ctrl.$valid;
25251             var prevModelValue = ctrl.$modelValue;
25252
25253             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25254
25255             ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25256               // If there was no change in validity, don't update the model
25257               // This prevents changing an invalid modelValue to undefined
25258               if (!allowInvalid && prevValid !== allValid) {
25259                 // Note: Don't check ctrl.$valid here, as we could have
25260                 // external validators (e.g. calculated on the server),
25261                 // that just call $setValidity and need the model value
25262                 // to calculate their validity.
25263                 ctrl.$modelValue = allValid ? modelValue : undefined;
25264
25265                 if (ctrl.$modelValue !== prevModelValue) {
25266                   ctrl.$$writeModelToScope();
25267                 }
25268               }
25269             });
25270
25271           };
25272
25273           this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25274             currentValidationRunId++;
25275             var localValidationRunId = currentValidationRunId;
25276
25277             // check parser error
25278             if (!processParseErrors()) {
25279               validationDone(false);
25280               return;
25281             }
25282             if (!processSyncValidators()) {
25283               validationDone(false);
25284               return;
25285             }
25286             processAsyncValidators();
25287
25288             function processParseErrors() {
25289               var errorKey = ctrl.$$parserName || 'parse';
25290               if (isUndefined(parserValid)) {
25291                 setValidity(errorKey, null);
25292               } else {
25293                 if (!parserValid) {
25294                   forEach(ctrl.$validators, function(v, name) {
25295                     setValidity(name, null);
25296                   });
25297                   forEach(ctrl.$asyncValidators, function(v, name) {
25298                     setValidity(name, null);
25299                   });
25300                 }
25301                 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25302                 setValidity(errorKey, parserValid);
25303                 return parserValid;
25304               }
25305               return true;
25306             }
25307
25308             function processSyncValidators() {
25309               var syncValidatorsValid = true;
25310               forEach(ctrl.$validators, function(validator, name) {
25311                 var result = validator(modelValue, viewValue);
25312                 syncValidatorsValid = syncValidatorsValid && result;
25313                 setValidity(name, result);
25314               });
25315               if (!syncValidatorsValid) {
25316                 forEach(ctrl.$asyncValidators, function(v, name) {
25317                   setValidity(name, null);
25318                 });
25319                 return false;
25320               }
25321               return true;
25322             }
25323
25324             function processAsyncValidators() {
25325               var validatorPromises = [];
25326               var allValid = true;
25327               forEach(ctrl.$asyncValidators, function(validator, name) {
25328                 var promise = validator(modelValue, viewValue);
25329                 if (!isPromiseLike(promise)) {
25330                   throw ngModelMinErr("$asyncValidators",
25331                     "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25332                 }
25333                 setValidity(name, undefined);
25334                 validatorPromises.push(promise.then(function() {
25335                   setValidity(name, true);
25336                 }, function(error) {
25337                   allValid = false;
25338                   setValidity(name, false);
25339                 }));
25340               });
25341               if (!validatorPromises.length) {
25342                 validationDone(true);
25343               } else {
25344                 $q.all(validatorPromises).then(function() {
25345                   validationDone(allValid);
25346                 }, noop);
25347               }
25348             }
25349
25350             function setValidity(name, isValid) {
25351               if (localValidationRunId === currentValidationRunId) {
25352                 ctrl.$setValidity(name, isValid);
25353               }
25354             }
25355
25356             function validationDone(allValid) {
25357               if (localValidationRunId === currentValidationRunId) {
25358
25359                 doneCallback(allValid);
25360               }
25361             }
25362           };
25363
25364           /**
25365            * @ngdoc method
25366            * @name ngModel.NgModelController#$commitViewValue
25367            *
25368            * @description
25369            * Commit a pending update to the `$modelValue`.
25370            *
25371            * Updates may be pending by a debounced event or because the input is waiting for a some future
25372            * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25373            * usually handles calling this in response to input events.
25374            */
25375           this.$commitViewValue = function() {
25376             var viewValue = ctrl.$viewValue;
25377
25378             $timeout.cancel(pendingDebounce);
25379
25380             // If the view value has not changed then we should just exit, except in the case where there is
25381             // a native validator on the element. In this case the validation state may have changed even though
25382             // the viewValue has stayed empty.
25383             if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25384               return;
25385             }
25386             ctrl.$$lastCommittedViewValue = viewValue;
25387
25388             // change to dirty
25389             if (ctrl.$pristine) {
25390               this.$setDirty();
25391             }
25392             this.$$parseAndValidate();
25393           };
25394
25395           this.$$parseAndValidate = function() {
25396             var viewValue = ctrl.$$lastCommittedViewValue;
25397             var modelValue = viewValue;
25398             parserValid = isUndefined(modelValue) ? undefined : true;
25399
25400             if (parserValid) {
25401               for (var i = 0; i < ctrl.$parsers.length; i++) {
25402                 modelValue = ctrl.$parsers[i](modelValue);
25403                 if (isUndefined(modelValue)) {
25404                   parserValid = false;
25405                   break;
25406                 }
25407               }
25408             }
25409             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25410               // ctrl.$modelValue has not been touched yet...
25411               ctrl.$modelValue = ngModelGet($scope);
25412             }
25413             var prevModelValue = ctrl.$modelValue;
25414             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25415             ctrl.$$rawModelValue = modelValue;
25416
25417             if (allowInvalid) {
25418               ctrl.$modelValue = modelValue;
25419               writeToModelIfNeeded();
25420             }
25421
25422             // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25423             // This can happen if e.g. $setViewValue is called from inside a parser
25424             ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25425               if (!allowInvalid) {
25426                 // Note: Don't check ctrl.$valid here, as we could have
25427                 // external validators (e.g. calculated on the server),
25428                 // that just call $setValidity and need the model value
25429                 // to calculate their validity.
25430                 ctrl.$modelValue = allValid ? modelValue : undefined;
25431                 writeToModelIfNeeded();
25432               }
25433             });
25434
25435             function writeToModelIfNeeded() {
25436               if (ctrl.$modelValue !== prevModelValue) {
25437                 ctrl.$$writeModelToScope();
25438               }
25439             }
25440           };
25441
25442           this.$$writeModelToScope = function() {
25443             ngModelSet($scope, ctrl.$modelValue);
25444             forEach(ctrl.$viewChangeListeners, function(listener) {
25445               try {
25446                 listener();
25447               } catch (e) {
25448                 $exceptionHandler(e);
25449               }
25450             });
25451           };
25452
25453           /**
25454            * @ngdoc method
25455            * @name ngModel.NgModelController#$setViewValue
25456            *
25457            * @description
25458            * Update the view value.
25459            *
25460            * This method should be called when a control wants to change the view value; typically,
25461            * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25462            * directive calls it when the value of the input changes and {@link ng.directive:select select}
25463            * calls it when an option is selected.
25464            *
25465            * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25466            * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25467            * value sent directly for processing, finally to be applied to `$modelValue` and then the
25468            * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25469            * in the `$viewChangeListeners` list, are called.
25470            *
25471            * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25472            * and the `default` trigger is not listed, all those actions will remain pending until one of the
25473            * `updateOn` events is triggered on the DOM element.
25474            * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25475            * directive is used with a custom debounce for this particular event.
25476            * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25477            * is specified, once the timer runs out.
25478            *
25479            * When used with standard inputs, the view value will always be a string (which is in some cases
25480            * parsed into another type, such as a `Date` object for `input[date]`.)
25481            * However, custom controls might also pass objects to this method. In this case, we should make
25482            * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25483            * perform a deep watch of objects, it only looks for a change of identity. If you only change
25484            * the property of the object then ngModel will not realise that the object has changed and
25485            * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25486            * not change properties of the copy once it has been passed to `$setViewValue`.
25487            * Otherwise you may cause the model value on the scope to change incorrectly.
25488            *
25489            * <div class="alert alert-info">
25490            * In any case, the value passed to the method should always reflect the current value
25491            * of the control. For example, if you are calling `$setViewValue` for an input element,
25492            * you should pass the input DOM value. Otherwise, the control and the scope model become
25493            * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25494            * the control's DOM value in any way. If we want to change the control's DOM value
25495            * programmatically, we should update the `ngModel` scope expression. Its new value will be
25496            * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25497            * to update the DOM, and finally call `$validate` on it.
25498            * </div>
25499            *
25500            * @param {*} value value from the view.
25501            * @param {string} trigger Event that triggered the update.
25502            */
25503           this.$setViewValue = function(value, trigger) {
25504             ctrl.$viewValue = value;
25505             if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25506               ctrl.$$debounceViewValueCommit(trigger);
25507             }
25508           };
25509
25510           this.$$debounceViewValueCommit = function(trigger) {
25511             var debounceDelay = 0,
25512                 options = ctrl.$options,
25513                 debounce;
25514
25515             if (options && isDefined(options.debounce)) {
25516               debounce = options.debounce;
25517               if (isNumber(debounce)) {
25518                 debounceDelay = debounce;
25519               } else if (isNumber(debounce[trigger])) {
25520                 debounceDelay = debounce[trigger];
25521               } else if (isNumber(debounce['default'])) {
25522                 debounceDelay = debounce['default'];
25523               }
25524             }
25525
25526             $timeout.cancel(pendingDebounce);
25527             if (debounceDelay) {
25528               pendingDebounce = $timeout(function() {
25529                 ctrl.$commitViewValue();
25530               }, debounceDelay);
25531             } else if ($rootScope.$$phase) {
25532               ctrl.$commitViewValue();
25533             } else {
25534               $scope.$apply(function() {
25535                 ctrl.$commitViewValue();
25536               });
25537             }
25538           };
25539
25540           // model -> value
25541           // Note: we cannot use a normal scope.$watch as we want to detect the following:
25542           // 1. scope value is 'a'
25543           // 2. user enters 'b'
25544           // 3. ng-change kicks in and reverts scope value to 'a'
25545           //    -> scope value did not change since the last digest as
25546           //       ng-change executes in apply phase
25547           // 4. view should be changed back to 'a'
25548           $scope.$watch(function ngModelWatch() {
25549             var modelValue = ngModelGet($scope);
25550
25551             // if scope model value and ngModel value are out of sync
25552             // TODO(perf): why not move this to the action fn?
25553             if (modelValue !== ctrl.$modelValue &&
25554                // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25555                (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25556             ) {
25557               ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25558               parserValid = undefined;
25559
25560               var formatters = ctrl.$formatters,
25561                   idx = formatters.length;
25562
25563               var viewValue = modelValue;
25564               while (idx--) {
25565                 viewValue = formatters[idx](viewValue);
25566               }
25567               if (ctrl.$viewValue !== viewValue) {
25568                 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25569                 ctrl.$render();
25570
25571                 ctrl.$$runValidators(modelValue, viewValue, noop);
25572               }
25573             }
25574
25575             return modelValue;
25576           });
25577         }];
25578
25579
25580         /**
25581          * @ngdoc directive
25582          * @name ngModel
25583          *
25584          * @element input
25585          * @priority 1
25586          *
25587          * @description
25588          * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25589          * property on the scope using {@link ngModel.NgModelController NgModelController},
25590          * which is created and exposed by this directive.
25591          *
25592          * `ngModel` is responsible for:
25593          *
25594          * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25595          *   require.
25596          * - Providing validation behavior (i.e. required, number, email, url).
25597          * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25598          * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25599          * - Registering the control with its parent {@link ng.directive:form form}.
25600          *
25601          * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25602          * current scope. If the property doesn't already exist on this scope, it will be created
25603          * implicitly and added to the scope.
25604          *
25605          * For best practices on using `ngModel`, see:
25606          *
25607          *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25608          *
25609          * For basic examples, how to use `ngModel`, see:
25610          *
25611          *  - {@link ng.directive:input input}
25612          *    - {@link input[text] text}
25613          *    - {@link input[checkbox] checkbox}
25614          *    - {@link input[radio] radio}
25615          *    - {@link input[number] number}
25616          *    - {@link input[email] email}
25617          *    - {@link input[url] url}
25618          *    - {@link input[date] date}
25619          *    - {@link input[datetime-local] datetime-local}
25620          *    - {@link input[time] time}
25621          *    - {@link input[month] month}
25622          *    - {@link input[week] week}
25623          *  - {@link ng.directive:select select}
25624          *  - {@link ng.directive:textarea textarea}
25625          *
25626          * # CSS classes
25627          * The following CSS classes are added and removed on the associated input/select/textarea element
25628          * depending on the validity of the model.
25629          *
25630          *  - `ng-valid`: the model is valid
25631          *  - `ng-invalid`: the model is invalid
25632          *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25633          *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25634          *  - `ng-pristine`: the control hasn't been interacted with yet
25635          *  - `ng-dirty`: the control has been interacted with
25636          *  - `ng-touched`: the control has been blurred
25637          *  - `ng-untouched`: the control hasn't been blurred
25638          *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25639          *
25640          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25641          *
25642          * ## Animation Hooks
25643          *
25644          * Animations within models are triggered when any of the associated CSS classes are added and removed
25645          * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25646          * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25647          * The animations that are triggered within ngModel are similar to how they work in ngClass and
25648          * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25649          *
25650          * The following example shows a simple way to utilize CSS transitions to style an input element
25651          * that has been rendered as invalid after it has been validated:
25652          *
25653          * <pre>
25654          * //be sure to include ngAnimate as a module to hook into more
25655          * //advanced animations
25656          * .my-input {
25657          *   transition:0.5s linear all;
25658          *   background: white;
25659          * }
25660          * .my-input.ng-invalid {
25661          *   background: red;
25662          *   color:white;
25663          * }
25664          * </pre>
25665          *
25666          * @example
25667          * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25668              <file name="index.html">
25669                <script>
25670                 angular.module('inputExample', [])
25671                   .controller('ExampleController', ['$scope', function($scope) {
25672                     $scope.val = '1';
25673                   }]);
25674                </script>
25675                <style>
25676                  .my-input {
25677                    transition:all linear 0.5s;
25678                    background: transparent;
25679                  }
25680                  .my-input.ng-invalid {
25681                    color:white;
25682                    background: red;
25683                  }
25684                </style>
25685                <p id="inputDescription">
25686                 Update input to see transitions when valid/invalid.
25687                 Integer is a valid value.
25688                </p>
25689                <form name="testForm" ng-controller="ExampleController">
25690                  <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25691                         aria-describedby="inputDescription" />
25692                </form>
25693              </file>
25694          * </example>
25695          *
25696          * ## Binding to a getter/setter
25697          *
25698          * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25699          * function that returns a representation of the model when called with zero arguments, and sets
25700          * the internal state of a model when called with an argument. It's sometimes useful to use this
25701          * for models that have an internal representation that's different from what the model exposes
25702          * to the view.
25703          *
25704          * <div class="alert alert-success">
25705          * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25706          * frequently than other parts of your code.
25707          * </div>
25708          *
25709          * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25710          * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25711          * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25712          * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25713          *
25714          * The following example shows how to use `ngModel` with a getter/setter:
25715          *
25716          * @example
25717          * <example name="ngModel-getter-setter" module="getterSetterExample">
25718              <file name="index.html">
25719                <div ng-controller="ExampleController">
25720                  <form name="userForm">
25721                    <label>Name:
25722                      <input type="text" name="userName"
25723                             ng-model="user.name"
25724                             ng-model-options="{ getterSetter: true }" />
25725                    </label>
25726                  </form>
25727                  <pre>user.name = <span ng-bind="user.name()"></span></pre>
25728                </div>
25729              </file>
25730              <file name="app.js">
25731                angular.module('getterSetterExample', [])
25732                  .controller('ExampleController', ['$scope', function($scope) {
25733                    var _name = 'Brian';
25734                    $scope.user = {
25735                      name: function(newName) {
25736                       // Note that newName can be undefined for two reasons:
25737                       // 1. Because it is called as a getter and thus called with no arguments
25738                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25739                       //    input is invalid
25740                       return arguments.length ? (_name = newName) : _name;
25741                      }
25742                    };
25743                  }]);
25744              </file>
25745          * </example>
25746          */
25747         var ngModelDirective = ['$rootScope', function($rootScope) {
25748           return {
25749             restrict: 'A',
25750             require: ['ngModel', '^?form', '^?ngModelOptions'],
25751             controller: NgModelController,
25752             // Prelink needs to run before any input directive
25753             // so that we can set the NgModelOptions in NgModelController
25754             // before anyone else uses it.
25755             priority: 1,
25756             compile: function ngModelCompile(element) {
25757               // Setup initial state of the control
25758               element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25759
25760               return {
25761                 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25762                   var modelCtrl = ctrls[0],
25763                       formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25764
25765                   modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25766
25767                   // notify others, especially parent forms
25768                   formCtrl.$addControl(modelCtrl);
25769
25770                   attr.$observe('name', function(newValue) {
25771                     if (modelCtrl.$name !== newValue) {
25772                       modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25773                     }
25774                   });
25775
25776                   scope.$on('$destroy', function() {
25777                     modelCtrl.$$parentForm.$removeControl(modelCtrl);
25778                   });
25779                 },
25780                 post: function ngModelPostLink(scope, element, attr, ctrls) {
25781                   var modelCtrl = ctrls[0];
25782                   if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25783                     element.on(modelCtrl.$options.updateOn, function(ev) {
25784                       modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25785                     });
25786                   }
25787
25788                   element.on('blur', function(ev) {
25789                     if (modelCtrl.$touched) return;
25790
25791                     if ($rootScope.$$phase) {
25792                       scope.$evalAsync(modelCtrl.$setTouched);
25793                     } else {
25794                       scope.$apply(modelCtrl.$setTouched);
25795                     }
25796                   });
25797                 }
25798               };
25799             }
25800           };
25801         }];
25802
25803         var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25804
25805         /**
25806          * @ngdoc directive
25807          * @name ngModelOptions
25808          *
25809          * @description
25810          * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25811          * events that will trigger a model update and/or a debouncing delay so that the actual update only
25812          * takes place when a timer expires; this timer will be reset after another change takes place.
25813          *
25814          * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25815          * be different from the value in the actual model. This means that if you update the model you
25816          * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25817          * order to make sure it is synchronized with the model and that any debounced action is canceled.
25818          *
25819          * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25820          * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25821          * important because `form` controllers are published to the related scope under the name in their
25822          * `name` attribute.
25823          *
25824          * Any pending changes will take place immediately when an enclosing form is submitted via the
25825          * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25826          * to have access to the updated model.
25827          *
25828          * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25829          *
25830          * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25831          *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25832          *     events using an space delimited list. There is a special event called `default` that
25833          *     matches the default events belonging of the control.
25834          *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25835          *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25836          *     custom value for each event. For example:
25837          *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25838          *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25839          *     not validate correctly instead of the default behavior of setting the model to undefined.
25840          *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25841                `ngModel` as getters/setters.
25842          *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25843          *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25844          *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25845          *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25846          *     If not specified, the timezone of the browser will be used.
25847          *
25848          * @example
25849
25850           The following example shows how to override immediate updates. Changes on the inputs within the
25851           form will update the model only when the control loses focus (blur event). If `escape` key is
25852           pressed while the input field is focused, the value is reset to the value in the current model.
25853
25854           <example name="ngModelOptions-directive-blur" module="optionsExample">
25855             <file name="index.html">
25856               <div ng-controller="ExampleController">
25857                 <form name="userForm">
25858                   <label>Name:
25859                     <input type="text" name="userName"
25860                            ng-model="user.name"
25861                            ng-model-options="{ updateOn: 'blur' }"
25862                            ng-keyup="cancel($event)" />
25863                   </label><br />
25864                   <label>Other data:
25865                     <input type="text" ng-model="user.data" />
25866                   </label><br />
25867                 </form>
25868                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25869                 <pre>user.data = <span ng-bind="user.data"></span></pre>
25870               </div>
25871             </file>
25872             <file name="app.js">
25873               angular.module('optionsExample', [])
25874                 .controller('ExampleController', ['$scope', function($scope) {
25875                   $scope.user = { name: 'John', data: '' };
25876
25877                   $scope.cancel = function(e) {
25878                     if (e.keyCode == 27) {
25879                       $scope.userForm.userName.$rollbackViewValue();
25880                     }
25881                   };
25882                 }]);
25883             </file>
25884             <file name="protractor.js" type="protractor">
25885               var model = element(by.binding('user.name'));
25886               var input = element(by.model('user.name'));
25887               var other = element(by.model('user.data'));
25888
25889               it('should allow custom events', function() {
25890                 input.sendKeys(' Doe');
25891                 input.click();
25892                 expect(model.getText()).toEqual('John');
25893                 other.click();
25894                 expect(model.getText()).toEqual('John Doe');
25895               });
25896
25897               it('should $rollbackViewValue when model changes', function() {
25898                 input.sendKeys(' Doe');
25899                 expect(input.getAttribute('value')).toEqual('John Doe');
25900                 input.sendKeys(protractor.Key.ESCAPE);
25901                 expect(input.getAttribute('value')).toEqual('John');
25902                 other.click();
25903                 expect(model.getText()).toEqual('John');
25904               });
25905             </file>
25906           </example>
25907
25908           This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25909           If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25910
25911           <example name="ngModelOptions-directive-debounce" module="optionsExample">
25912             <file name="index.html">
25913               <div ng-controller="ExampleController">
25914                 <form name="userForm">
25915                   <label>Name:
25916                     <input type="text" name="userName"
25917                            ng-model="user.name"
25918                            ng-model-options="{ debounce: 1000 }" />
25919                   </label>
25920                   <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25921                   <br />
25922                 </form>
25923                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25924               </div>
25925             </file>
25926             <file name="app.js">
25927               angular.module('optionsExample', [])
25928                 .controller('ExampleController', ['$scope', function($scope) {
25929                   $scope.user = { name: 'Igor' };
25930                 }]);
25931             </file>
25932           </example>
25933
25934           This one shows how to bind to getter/setters:
25935
25936           <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25937             <file name="index.html">
25938               <div ng-controller="ExampleController">
25939                 <form name="userForm">
25940                   <label>Name:
25941                     <input type="text" name="userName"
25942                            ng-model="user.name"
25943                            ng-model-options="{ getterSetter: true }" />
25944                   </label>
25945                 </form>
25946                 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25947               </div>
25948             </file>
25949             <file name="app.js">
25950               angular.module('getterSetterExample', [])
25951                 .controller('ExampleController', ['$scope', function($scope) {
25952                   var _name = 'Brian';
25953                   $scope.user = {
25954                     name: function(newName) {
25955                       // Note that newName can be undefined for two reasons:
25956                       // 1. Because it is called as a getter and thus called with no arguments
25957                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25958                       //    input is invalid
25959                       return arguments.length ? (_name = newName) : _name;
25960                     }
25961                   };
25962                 }]);
25963             </file>
25964           </example>
25965          */
25966         var ngModelOptionsDirective = function() {
25967           return {
25968             restrict: 'A',
25969             controller: ['$scope', '$attrs', function($scope, $attrs) {
25970               var that = this;
25971               this.$options = copy($scope.$eval($attrs.ngModelOptions));
25972               // Allow adding/overriding bound events
25973               if (isDefined(this.$options.updateOn)) {
25974                 this.$options.updateOnDefault = false;
25975                 // extract "default" pseudo-event from list of events that can trigger a model update
25976                 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25977                   that.$options.updateOnDefault = true;
25978                   return ' ';
25979                 }));
25980               } else {
25981                 this.$options.updateOnDefault = true;
25982               }
25983             }]
25984           };
25985         };
25986
25987
25988
25989         // helper methods
25990         function addSetValidityMethod(context) {
25991           var ctrl = context.ctrl,
25992               $element = context.$element,
25993               classCache = {},
25994               set = context.set,
25995               unset = context.unset,
25996               $animate = context.$animate;
25997
25998           classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25999
26000           ctrl.$setValidity = setValidity;
26001
26002           function setValidity(validationErrorKey, state, controller) {
26003             if (isUndefined(state)) {
26004               createAndSet('$pending', validationErrorKey, controller);
26005             } else {
26006               unsetAndCleanup('$pending', validationErrorKey, controller);
26007             }
26008             if (!isBoolean(state)) {
26009               unset(ctrl.$error, validationErrorKey, controller);
26010               unset(ctrl.$$success, validationErrorKey, controller);
26011             } else {
26012               if (state) {
26013                 unset(ctrl.$error, validationErrorKey, controller);
26014                 set(ctrl.$$success, validationErrorKey, controller);
26015               } else {
26016                 set(ctrl.$error, validationErrorKey, controller);
26017                 unset(ctrl.$$success, validationErrorKey, controller);
26018               }
26019             }
26020             if (ctrl.$pending) {
26021               cachedToggleClass(PENDING_CLASS, true);
26022               ctrl.$valid = ctrl.$invalid = undefined;
26023               toggleValidationCss('', null);
26024             } else {
26025               cachedToggleClass(PENDING_CLASS, false);
26026               ctrl.$valid = isObjectEmpty(ctrl.$error);
26027               ctrl.$invalid = !ctrl.$valid;
26028               toggleValidationCss('', ctrl.$valid);
26029             }
26030
26031             // re-read the state as the set/unset methods could have
26032             // combined state in ctrl.$error[validationError] (used for forms),
26033             // where setting/unsetting only increments/decrements the value,
26034             // and does not replace it.
26035             var combinedState;
26036             if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26037               combinedState = undefined;
26038             } else if (ctrl.$error[validationErrorKey]) {
26039               combinedState = false;
26040             } else if (ctrl.$$success[validationErrorKey]) {
26041               combinedState = true;
26042             } else {
26043               combinedState = null;
26044             }
26045
26046             toggleValidationCss(validationErrorKey, combinedState);
26047             ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26048           }
26049
26050           function createAndSet(name, value, controller) {
26051             if (!ctrl[name]) {
26052               ctrl[name] = {};
26053             }
26054             set(ctrl[name], value, controller);
26055           }
26056
26057           function unsetAndCleanup(name, value, controller) {
26058             if (ctrl[name]) {
26059               unset(ctrl[name], value, controller);
26060             }
26061             if (isObjectEmpty(ctrl[name])) {
26062               ctrl[name] = undefined;
26063             }
26064           }
26065
26066           function cachedToggleClass(className, switchValue) {
26067             if (switchValue && !classCache[className]) {
26068               $animate.addClass($element, className);
26069               classCache[className] = true;
26070             } else if (!switchValue && classCache[className]) {
26071               $animate.removeClass($element, className);
26072               classCache[className] = false;
26073             }
26074           }
26075
26076           function toggleValidationCss(validationErrorKey, isValid) {
26077             validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26078
26079             cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26080             cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26081           }
26082         }
26083
26084         function isObjectEmpty(obj) {
26085           if (obj) {
26086             for (var prop in obj) {
26087               if (obj.hasOwnProperty(prop)) {
26088                 return false;
26089               }
26090             }
26091           }
26092           return true;
26093         }
26094
26095         /**
26096          * @ngdoc directive
26097          * @name ngNonBindable
26098          * @restrict AC
26099          * @priority 1000
26100          *
26101          * @description
26102          * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26103          * DOM element. This is useful if the element contains what appears to be Angular directives and
26104          * bindings but which should be ignored by Angular. This could be the case if you have a site that
26105          * displays snippets of code, for instance.
26106          *
26107          * @element ANY
26108          *
26109          * @example
26110          * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26111          * but the one wrapped in `ngNonBindable` is left alone.
26112          *
26113          * @example
26114             <example>
26115               <file name="index.html">
26116                 <div>Normal: {{1 + 2}}</div>
26117                 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26118               </file>
26119               <file name="protractor.js" type="protractor">
26120                it('should check ng-non-bindable', function() {
26121                  expect(element(by.binding('1 + 2')).getText()).toContain('3');
26122                  expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26123                });
26124               </file>
26125             </example>
26126          */
26127         var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26128
26129         /* global jqLiteRemove */
26130
26131         var ngOptionsMinErr = minErr('ngOptions');
26132
26133         /**
26134          * @ngdoc directive
26135          * @name ngOptions
26136          * @restrict A
26137          *
26138          * @description
26139          *
26140          * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26141          * elements for the `<select>` element using the array or object obtained by evaluating the
26142          * `ngOptions` comprehension expression.
26143          *
26144          * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26145          * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26146          * increasing speed by not creating a new scope for each repeated instance, as well as providing
26147          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26148          * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26149          *  to a non-string value. This is because an option element can only be bound to string values at
26150          * present.
26151          *
26152          * When an item in the `<select>` menu is selected, the array element or object property
26153          * represented by the selected option will be bound to the model identified by the `ngModel`
26154          * directive.
26155          *
26156          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26157          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26158          * option. See example below for demonstration.
26159          *
26160          * ## Complex Models (objects or collections)
26161          *
26162          * By default, `ngModel` watches the model by reference, not value. This is important to know when
26163          * binding the select to a model that is an object or a collection.
26164          *
26165          * One issue occurs if you want to preselect an option. For example, if you set
26166          * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26167          * because the objects are not identical. So by default, you should always reference the item in your collection
26168          * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26169          *
26170          * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26171          * of the item not by reference, but by the result of the `track by` expression. For example, if your
26172          * collection items have an id property, you would `track by item.id`.
26173          *
26174          * A different issue with objects or collections is that ngModel won't detect if an object property or
26175          * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26176          * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26177          * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26178          * has not changed identity, but only a property on the object or an item in the collection changes.
26179          *
26180          * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26181          * if the model is an array). This means that changing a property deeper than the first level inside the
26182          * object/collection will not trigger a re-rendering.
26183          *
26184          * ## `select` **`as`**
26185          *
26186          * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26187          * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26188          * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26189          * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26190          *
26191          *
26192          * ### `select` **`as`** and **`track by`**
26193          *
26194          * <div class="alert alert-warning">
26195          * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26196          * </div>
26197          *
26198          * Given this array of items on the $scope:
26199          *
26200          * ```js
26201          * $scope.items = [{
26202          *   id: 1,
26203          *   label: 'aLabel',
26204          *   subItem: { name: 'aSubItem' }
26205          * }, {
26206          *   id: 2,
26207          *   label: 'bLabel',
26208          *   subItem: { name: 'bSubItem' }
26209          * }];
26210          * ```
26211          *
26212          * This will work:
26213          *
26214          * ```html
26215          * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26216          * ```
26217          * ```js
26218          * $scope.selected = $scope.items[0];
26219          * ```
26220          *
26221          * but this will not work:
26222          *
26223          * ```html
26224          * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26225          * ```
26226          * ```js
26227          * $scope.selected = $scope.items[0].subItem;
26228          * ```
26229          *
26230          * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26231          * `items` array. Because the selected option has been set programmatically in the controller, the
26232          * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26233          * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26234          * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26235          * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26236          * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26237          *
26238          *
26239          * @param {string} ngModel Assignable angular expression to data-bind to.
26240          * @param {string=} name Property name of the form under which the control is published.
26241          * @param {string=} required The control is considered valid only if value is entered.
26242          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26243          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26244          *    `required` when you want to data-bind to the `required` attribute.
26245          * @param {comprehension_expression=} ngOptions in one of the following forms:
26246          *
26247          *   * for array data sources:
26248          *     * `label` **`for`** `value` **`in`** `array`
26249          *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26250          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26251          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26252          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26253          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26254          *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26255          *        (for including a filter with `track by`)
26256          *   * for object data sources:
26257          *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26258          *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26259          *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26260          *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26261          *     * `select` **`as`** `label` **`group by`** `group`
26262          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26263          *     * `select` **`as`** `label` **`disable when`** `disable`
26264          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26265          *
26266          * Where:
26267          *
26268          *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26269          *   * `value`: local variable which will refer to each item in the `array` or each property value
26270          *      of `object` during iteration.
26271          *   * `key`: local variable which will refer to a property name in `object` during iteration.
26272          *   * `label`: The result of this expression will be the label for `<option>` element. The
26273          *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26274          *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26275          *      element. If not specified, `select` expression will default to `value`.
26276          *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26277          *      DOM element.
26278          *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26279          *      element. Return `true` to disable.
26280          *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26281          *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26282          *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26283          *      even when the options are recreated (e.g. reloaded from the server).
26284          *
26285          * @example
26286             <example module="selectExample">
26287               <file name="index.html">
26288                 <script>
26289                 angular.module('selectExample', [])
26290                   .controller('ExampleController', ['$scope', function($scope) {
26291                     $scope.colors = [
26292                       {name:'black', shade:'dark'},
26293                       {name:'white', shade:'light', notAnOption: true},
26294                       {name:'red', shade:'dark'},
26295                       {name:'blue', shade:'dark', notAnOption: true},
26296                       {name:'yellow', shade:'light', notAnOption: false}
26297                     ];
26298                     $scope.myColor = $scope.colors[2]; // red
26299                   }]);
26300                 </script>
26301                 <div ng-controller="ExampleController">
26302                   <ul>
26303                     <li ng-repeat="color in colors">
26304                       <label>Name: <input ng-model="color.name"></label>
26305                       <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26306                       <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26307                     </li>
26308                     <li>
26309                       <button ng-click="colors.push({})">add</button>
26310                     </li>
26311                   </ul>
26312                   <hr/>
26313                   <label>Color (null not allowed):
26314                     <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26315                   </label><br/>
26316                   <label>Color (null allowed):
26317                   <span  class="nullable">
26318                     <select ng-model="myColor" ng-options="color.name for color in colors">
26319                       <option value="">-- choose color --</option>
26320                     </select>
26321                   </span></label><br/>
26322
26323                   <label>Color grouped by shade:
26324                     <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26325                     </select>
26326                   </label><br/>
26327
26328                   <label>Color grouped by shade, with some disabled:
26329                     <select ng-model="myColor"
26330                           ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26331                     </select>
26332                   </label><br/>
26333
26334
26335
26336                   Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26337                   <br/>
26338                   <hr/>
26339                   Currently selected: {{ {selected_color:myColor} }}
26340                   <div style="border:solid 1px black; height:20px"
26341                        ng-style="{'background-color':myColor.name}">
26342                   </div>
26343                 </div>
26344               </file>
26345               <file name="protractor.js" type="protractor">
26346                  it('should check ng-options', function() {
26347                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26348                    element.all(by.model('myColor')).first().click();
26349                    element.all(by.css('select[ng-model="myColor"] option')).first().click();
26350                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26351                    element(by.css('.nullable select[ng-model="myColor"]')).click();
26352                    element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26353                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26354                  });
26355               </file>
26356             </example>
26357          */
26358
26359         // jshint maxlen: false
26360         //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26361         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]+?))?$/;
26362                                 // 1: value expression (valueFn)
26363                                 // 2: label expression (displayFn)
26364                                 // 3: group by expression (groupByFn)
26365                                 // 4: disable when expression (disableWhenFn)
26366                                 // 5: array item variable name
26367                                 // 6: object item key variable name
26368                                 // 7: object item value variable name
26369                                 // 8: collection expression
26370                                 // 9: track by expression
26371         // jshint maxlen: 100
26372
26373
26374         var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26375
26376           function parseOptionsExpression(optionsExp, selectElement, scope) {
26377
26378             var match = optionsExp.match(NG_OPTIONS_REGEXP);
26379             if (!(match)) {
26380               throw ngOptionsMinErr('iexp',
26381                 "Expected expression in form of " +
26382                 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26383                 " but got '{0}'. Element: {1}",
26384                 optionsExp, startingTag(selectElement));
26385             }
26386
26387             // Extract the parts from the ngOptions expression
26388
26389             // The variable name for the value of the item in the collection
26390             var valueName = match[5] || match[7];
26391             // The variable name for the key of the item in the collection
26392             var keyName = match[6];
26393
26394             // An expression that generates the viewValue for an option if there is a label expression
26395             var selectAs = / as /.test(match[0]) && match[1];
26396             // An expression that is used to track the id of each object in the options collection
26397             var trackBy = match[9];
26398             // An expression that generates the viewValue for an option if there is no label expression
26399             var valueFn = $parse(match[2] ? match[1] : valueName);
26400             var selectAsFn = selectAs && $parse(selectAs);
26401             var viewValueFn = selectAsFn || valueFn;
26402             var trackByFn = trackBy && $parse(trackBy);
26403
26404             // Get the value by which we are going to track the option
26405             // if we have a trackFn then use that (passing scope and locals)
26406             // otherwise just hash the given viewValue
26407             var getTrackByValueFn = trackBy ?
26408                                       function(value, locals) { return trackByFn(scope, locals); } :
26409                                       function getHashOfValue(value) { return hashKey(value); };
26410             var getTrackByValue = function(value, key) {
26411               return getTrackByValueFn(value, getLocals(value, key));
26412             };
26413
26414             var displayFn = $parse(match[2] || match[1]);
26415             var groupByFn = $parse(match[3] || '');
26416             var disableWhenFn = $parse(match[4] || '');
26417             var valuesFn = $parse(match[8]);
26418
26419             var locals = {};
26420             var getLocals = keyName ? function(value, key) {
26421               locals[keyName] = key;
26422               locals[valueName] = value;
26423               return locals;
26424             } : function(value) {
26425               locals[valueName] = value;
26426               return locals;
26427             };
26428
26429
26430             function Option(selectValue, viewValue, label, group, disabled) {
26431               this.selectValue = selectValue;
26432               this.viewValue = viewValue;
26433               this.label = label;
26434               this.group = group;
26435               this.disabled = disabled;
26436             }
26437
26438             function getOptionValuesKeys(optionValues) {
26439               var optionValuesKeys;
26440
26441               if (!keyName && isArrayLike(optionValues)) {
26442                 optionValuesKeys = optionValues;
26443               } else {
26444                 // if object, extract keys, in enumeration order, unsorted
26445                 optionValuesKeys = [];
26446                 for (var itemKey in optionValues) {
26447                   if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26448                     optionValuesKeys.push(itemKey);
26449                   }
26450                 }
26451               }
26452               return optionValuesKeys;
26453             }
26454
26455             return {
26456               trackBy: trackBy,
26457               getTrackByValue: getTrackByValue,
26458               getWatchables: $parse(valuesFn, function(optionValues) {
26459                 // Create a collection of things that we would like to watch (watchedArray)
26460                 // so that they can all be watched using a single $watchCollection
26461                 // that only runs the handler once if anything changes
26462                 var watchedArray = [];
26463                 optionValues = optionValues || [];
26464
26465                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26466                 var optionValuesLength = optionValuesKeys.length;
26467                 for (var index = 0; index < optionValuesLength; index++) {
26468                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26469                   var value = optionValues[key];
26470
26471                   var locals = getLocals(optionValues[key], key);
26472                   var selectValue = getTrackByValueFn(optionValues[key], locals);
26473                   watchedArray.push(selectValue);
26474
26475                   // Only need to watch the displayFn if there is a specific label expression
26476                   if (match[2] || match[1]) {
26477                     var label = displayFn(scope, locals);
26478                     watchedArray.push(label);
26479                   }
26480
26481                   // Only need to watch the disableWhenFn if there is a specific disable expression
26482                   if (match[4]) {
26483                     var disableWhen = disableWhenFn(scope, locals);
26484                     watchedArray.push(disableWhen);
26485                   }
26486                 }
26487                 return watchedArray;
26488               }),
26489
26490               getOptions: function() {
26491
26492                 var optionItems = [];
26493                 var selectValueMap = {};
26494
26495                 // The option values were already computed in the `getWatchables` fn,
26496                 // which must have been called to trigger `getOptions`
26497                 var optionValues = valuesFn(scope) || [];
26498                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26499                 var optionValuesLength = optionValuesKeys.length;
26500
26501                 for (var index = 0; index < optionValuesLength; index++) {
26502                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26503                   var value = optionValues[key];
26504                   var locals = getLocals(value, key);
26505                   var viewValue = viewValueFn(scope, locals);
26506                   var selectValue = getTrackByValueFn(viewValue, locals);
26507                   var label = displayFn(scope, locals);
26508                   var group = groupByFn(scope, locals);
26509                   var disabled = disableWhenFn(scope, locals);
26510                   var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26511
26512                   optionItems.push(optionItem);
26513                   selectValueMap[selectValue] = optionItem;
26514                 }
26515
26516                 return {
26517                   items: optionItems,
26518                   selectValueMap: selectValueMap,
26519                   getOptionFromViewValue: function(value) {
26520                     return selectValueMap[getTrackByValue(value)];
26521                   },
26522                   getViewValueFromOption: function(option) {
26523                     // If the viewValue could be an object that may be mutated by the application,
26524                     // we need to make a copy and not return the reference to the value on the option.
26525                     return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26526                   }
26527                 };
26528               }
26529             };
26530           }
26531
26532
26533           // we can't just jqLite('<option>') since jqLite is not smart enough
26534           // to create it in <select> and IE barfs otherwise.
26535           var optionTemplate = document.createElement('option'),
26536               optGroupTemplate = document.createElement('optgroup');
26537
26538
26539             function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26540
26541               // if ngModel is not defined, we don't need to do anything
26542               var ngModelCtrl = ctrls[1];
26543               if (!ngModelCtrl) return;
26544
26545               var selectCtrl = ctrls[0];
26546               var multiple = attr.multiple;
26547
26548               // The emptyOption allows the application developer to provide their own custom "empty"
26549               // option when the viewValue does not match any of the option values.
26550               var emptyOption;
26551               for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26552                 if (children[i].value === '') {
26553                   emptyOption = children.eq(i);
26554                   break;
26555                 }
26556               }
26557
26558               var providedEmptyOption = !!emptyOption;
26559
26560               var unknownOption = jqLite(optionTemplate.cloneNode(false));
26561               unknownOption.val('?');
26562
26563               var options;
26564               var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26565
26566
26567               var renderEmptyOption = function() {
26568                 if (!providedEmptyOption) {
26569                   selectElement.prepend(emptyOption);
26570                 }
26571                 selectElement.val('');
26572                 emptyOption.prop('selected', true); // needed for IE
26573                 emptyOption.attr('selected', true);
26574               };
26575
26576               var removeEmptyOption = function() {
26577                 if (!providedEmptyOption) {
26578                   emptyOption.remove();
26579                 }
26580               };
26581
26582
26583               var renderUnknownOption = function() {
26584                 selectElement.prepend(unknownOption);
26585                 selectElement.val('?');
26586                 unknownOption.prop('selected', true); // needed for IE
26587                 unknownOption.attr('selected', true);
26588               };
26589
26590               var removeUnknownOption = function() {
26591                 unknownOption.remove();
26592               };
26593
26594               // Update the controller methods for multiple selectable options
26595               if (!multiple) {
26596
26597                 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26598                   var option = options.getOptionFromViewValue(value);
26599
26600                   if (option && !option.disabled) {
26601                     if (selectElement[0].value !== option.selectValue) {
26602                       removeUnknownOption();
26603                       removeEmptyOption();
26604
26605                       selectElement[0].value = option.selectValue;
26606                       option.element.selected = true;
26607                       option.element.setAttribute('selected', 'selected');
26608                     }
26609                   } else {
26610                     if (value === null || providedEmptyOption) {
26611                       removeUnknownOption();
26612                       renderEmptyOption();
26613                     } else {
26614                       removeEmptyOption();
26615                       renderUnknownOption();
26616                     }
26617                   }
26618                 };
26619
26620                 selectCtrl.readValue = function readNgOptionsValue() {
26621
26622                   var selectedOption = options.selectValueMap[selectElement.val()];
26623
26624                   if (selectedOption && !selectedOption.disabled) {
26625                     removeEmptyOption();
26626                     removeUnknownOption();
26627                     return options.getViewValueFromOption(selectedOption);
26628                   }
26629                   return null;
26630                 };
26631
26632                 // If we are using `track by` then we must watch the tracked value on the model
26633                 // since ngModel only watches for object identity change
26634                 if (ngOptions.trackBy) {
26635                   scope.$watch(
26636                     function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26637                     function() { ngModelCtrl.$render(); }
26638                   );
26639                 }
26640
26641               } else {
26642
26643                 ngModelCtrl.$isEmpty = function(value) {
26644                   return !value || value.length === 0;
26645                 };
26646
26647
26648                 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26649                   options.items.forEach(function(option) {
26650                     option.element.selected = false;
26651                   });
26652
26653                   if (value) {
26654                     value.forEach(function(item) {
26655                       var option = options.getOptionFromViewValue(item);
26656                       if (option && !option.disabled) option.element.selected = true;
26657                     });
26658                   }
26659                 };
26660
26661
26662                 selectCtrl.readValue = function readNgOptionsMultiple() {
26663                   var selectedValues = selectElement.val() || [],
26664                       selections = [];
26665
26666                   forEach(selectedValues, function(value) {
26667                     var option = options.selectValueMap[value];
26668                     if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26669                   });
26670
26671                   return selections;
26672                 };
26673
26674                 // If we are using `track by` then we must watch these tracked values on the model
26675                 // since ngModel only watches for object identity change
26676                 if (ngOptions.trackBy) {
26677
26678                   scope.$watchCollection(function() {
26679                     if (isArray(ngModelCtrl.$viewValue)) {
26680                       return ngModelCtrl.$viewValue.map(function(value) {
26681                         return ngOptions.getTrackByValue(value);
26682                       });
26683                     }
26684                   }, function() {
26685                     ngModelCtrl.$render();
26686                   });
26687
26688                 }
26689               }
26690
26691
26692               if (providedEmptyOption) {
26693
26694                 // we need to remove it before calling selectElement.empty() because otherwise IE will
26695                 // remove the label from the element. wtf?
26696                 emptyOption.remove();
26697
26698                 // compile the element since there might be bindings in it
26699                 $compile(emptyOption)(scope);
26700
26701                 // remove the class, which is added automatically because we recompile the element and it
26702                 // becomes the compilation root
26703                 emptyOption.removeClass('ng-scope');
26704               } else {
26705                 emptyOption = jqLite(optionTemplate.cloneNode(false));
26706               }
26707
26708               // We need to do this here to ensure that the options object is defined
26709               // when we first hit it in writeNgOptionsValue
26710               updateOptions();
26711
26712               // We will re-render the option elements if the option values or labels change
26713               scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26714
26715               // ------------------------------------------------------------------ //
26716
26717
26718               function updateOptionElement(option, element) {
26719                 option.element = element;
26720                 element.disabled = option.disabled;
26721                 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26722                 // selects in certain circumstances when multiple selects are next to each other and display
26723                 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26724                 // See https://github.com/angular/angular.js/issues/11314 for more info.
26725                 // This is unfortunately untestable with unit / e2e tests
26726                 if (option.label !== element.label) {
26727                   element.label = option.label;
26728                   element.textContent = option.label;
26729                 }
26730                 if (option.value !== element.value) element.value = option.selectValue;
26731               }
26732
26733               function addOrReuseElement(parent, current, type, templateElement) {
26734                 var element;
26735                 // Check whether we can reuse the next element
26736                 if (current && lowercase(current.nodeName) === type) {
26737                   // The next element is the right type so reuse it
26738                   element = current;
26739                 } else {
26740                   // The next element is not the right type so create a new one
26741                   element = templateElement.cloneNode(false);
26742                   if (!current) {
26743                     // There are no more elements so just append it to the select
26744                     parent.appendChild(element);
26745                   } else {
26746                     // The next element is not a group so insert the new one
26747                     parent.insertBefore(element, current);
26748                   }
26749                 }
26750                 return element;
26751               }
26752
26753
26754               function removeExcessElements(current) {
26755                 var next;
26756                 while (current) {
26757                   next = current.nextSibling;
26758                   jqLiteRemove(current);
26759                   current = next;
26760                 }
26761               }
26762
26763
26764               function skipEmptyAndUnknownOptions(current) {
26765                 var emptyOption_ = emptyOption && emptyOption[0];
26766                 var unknownOption_ = unknownOption && unknownOption[0];
26767
26768                 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26769                 // because the compiled empty option might have been replaced by a comment because
26770                 // it had an "element" transclusion directive on it (such as ngIf)
26771                 if (emptyOption_ || unknownOption_) {
26772                   while (current &&
26773                         (current === emptyOption_ ||
26774                         current === unknownOption_ ||
26775                         current.nodeType === NODE_TYPE_COMMENT ||
26776                         current.value === '')) {
26777                     current = current.nextSibling;
26778                   }
26779                 }
26780                 return current;
26781               }
26782
26783
26784               function updateOptions() {
26785
26786                 var previousValue = options && selectCtrl.readValue();
26787
26788                 options = ngOptions.getOptions();
26789
26790                 var groupMap = {};
26791                 var currentElement = selectElement[0].firstChild;
26792
26793                 // Ensure that the empty option is always there if it was explicitly provided
26794                 if (providedEmptyOption) {
26795                   selectElement.prepend(emptyOption);
26796                 }
26797
26798                 currentElement = skipEmptyAndUnknownOptions(currentElement);
26799
26800                 options.items.forEach(function updateOption(option) {
26801                   var group;
26802                   var groupElement;
26803                   var optionElement;
26804
26805                   if (option.group) {
26806
26807                     // This option is to live in a group
26808                     // See if we have already created this group
26809                     group = groupMap[option.group];
26810
26811                     if (!group) {
26812
26813                       // We have not already created this group
26814                       groupElement = addOrReuseElement(selectElement[0],
26815                                                        currentElement,
26816                                                        'optgroup',
26817                                                        optGroupTemplate);
26818                       // Move to the next element
26819                       currentElement = groupElement.nextSibling;
26820
26821                       // Update the label on the group element
26822                       groupElement.label = option.group;
26823
26824                       // Store it for use later
26825                       group = groupMap[option.group] = {
26826                         groupElement: groupElement,
26827                         currentOptionElement: groupElement.firstChild
26828                       };
26829
26830                     }
26831
26832                     // So now we have a group for this option we add the option to the group
26833                     optionElement = addOrReuseElement(group.groupElement,
26834                                                       group.currentOptionElement,
26835                                                       'option',
26836                                                       optionTemplate);
26837                     updateOptionElement(option, optionElement);
26838                     // Move to the next element
26839                     group.currentOptionElement = optionElement.nextSibling;
26840
26841                   } else {
26842
26843                     // This option is not in a group
26844                     optionElement = addOrReuseElement(selectElement[0],
26845                                                       currentElement,
26846                                                       'option',
26847                                                       optionTemplate);
26848                     updateOptionElement(option, optionElement);
26849                     // Move to the next element
26850                     currentElement = optionElement.nextSibling;
26851                   }
26852                 });
26853
26854
26855                 // Now remove all excess options and group
26856                 Object.keys(groupMap).forEach(function(key) {
26857                   removeExcessElements(groupMap[key].currentOptionElement);
26858                 });
26859                 removeExcessElements(currentElement);
26860
26861                 ngModelCtrl.$render();
26862
26863                 // Check to see if the value has changed due to the update to the options
26864                 if (!ngModelCtrl.$isEmpty(previousValue)) {
26865                   var nextValue = selectCtrl.readValue();
26866                   if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26867                     ngModelCtrl.$setViewValue(nextValue);
26868                     ngModelCtrl.$render();
26869                   }
26870                 }
26871
26872               }
26873           }
26874
26875           return {
26876             restrict: 'A',
26877             terminal: true,
26878             require: ['select', '?ngModel'],
26879             link: {
26880               pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26881                 // Deactivate the SelectController.register method to prevent
26882                 // option directives from accidentally registering themselves
26883                 // (and unwanted $destroy handlers etc.)
26884                 ctrls[0].registerOption = noop;
26885               },
26886               post: ngOptionsPostLink
26887             }
26888           };
26889         }];
26890
26891         /**
26892          * @ngdoc directive
26893          * @name ngPluralize
26894          * @restrict EA
26895          *
26896          * @description
26897          * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26898          * These rules are bundled with angular.js, but can be overridden
26899          * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26900          * by specifying the mappings between
26901          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26902          * and the strings to be displayed.
26903          *
26904          * # Plural categories and explicit number rules
26905          * There are two
26906          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26907          * in Angular's default en-US locale: "one" and "other".
26908          *
26909          * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26910          * any number that is not 1), an explicit number rule can only match one number. For example, the
26911          * explicit number rule for "3" matches the number 3. There are examples of plural categories
26912          * and explicit number rules throughout the rest of this documentation.
26913          *
26914          * # Configuring ngPluralize
26915          * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26916          * You can also provide an optional attribute, `offset`.
26917          *
26918          * The value of the `count` attribute can be either a string or an {@link guide/expression
26919          * Angular expression}; these are evaluated on the current scope for its bound value.
26920          *
26921          * The `when` attribute specifies the mappings between plural categories and the actual
26922          * string to be displayed. The value of the attribute should be a JSON object.
26923          *
26924          * The following example shows how to configure ngPluralize:
26925          *
26926          * ```html
26927          * <ng-pluralize count="personCount"
26928                          when="{'0': 'Nobody is viewing.',
26929          *                      'one': '1 person is viewing.',
26930          *                      'other': '{} people are viewing.'}">
26931          * </ng-pluralize>
26932          *```
26933          *
26934          * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26935          * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26936          * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26937          * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26938          * show "a dozen people are viewing".
26939          *
26940          * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26941          * into pluralized strings. In the previous example, Angular will replace `{}` with
26942          * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26943          * for <span ng-non-bindable>{{numberExpression}}</span>.
26944          *
26945          * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26946          * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26947          *
26948          * # Configuring ngPluralize with offset
26949          * The `offset` attribute allows further customization of pluralized text, which can result in
26950          * a better user experience. For example, instead of the message "4 people are viewing this document",
26951          * you might display "John, Kate and 2 others are viewing this document".
26952          * The offset attribute allows you to offset a number by any desired value.
26953          * Let's take a look at an example:
26954          *
26955          * ```html
26956          * <ng-pluralize count="personCount" offset=2
26957          *               when="{'0': 'Nobody is viewing.',
26958          *                      '1': '{{person1}} is viewing.',
26959          *                      '2': '{{person1}} and {{person2}} are viewing.',
26960          *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26961          *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26962          * </ng-pluralize>
26963          * ```
26964          *
26965          * Notice that we are still using two plural categories(one, other), but we added
26966          * three explicit number rules 0, 1 and 2.
26967          * When one person, perhaps John, views the document, "John is viewing" will be shown.
26968          * When three people view the document, no explicit number rule is found, so
26969          * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26970          * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26971          * is shown.
26972          *
26973          * Note that when you specify offsets, you must provide explicit number rules for
26974          * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26975          * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26976          * plural categories "one" and "other".
26977          *
26978          * @param {string|expression} count The variable to be bound to.
26979          * @param {string} when The mapping between plural category to its corresponding strings.
26980          * @param {number=} offset Offset to deduct from the total number.
26981          *
26982          * @example
26983             <example module="pluralizeExample">
26984               <file name="index.html">
26985                 <script>
26986                   angular.module('pluralizeExample', [])
26987                     .controller('ExampleController', ['$scope', function($scope) {
26988                       $scope.person1 = 'Igor';
26989                       $scope.person2 = 'Misko';
26990                       $scope.personCount = 1;
26991                     }]);
26992                 </script>
26993                 <div ng-controller="ExampleController">
26994                   <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26995                   <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26996                   <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26997
26998                   <!--- Example with simple pluralization rules for en locale --->
26999                   Without Offset:
27000                   <ng-pluralize count="personCount"
27001                                 when="{'0': 'Nobody is viewing.',
27002                                        'one': '1 person is viewing.',
27003                                        'other': '{} people are viewing.'}">
27004                   </ng-pluralize><br>
27005
27006                   <!--- Example with offset --->
27007                   With Offset(2):
27008                   <ng-pluralize count="personCount" offset=2
27009                                 when="{'0': 'Nobody is viewing.',
27010                                        '1': '{{person1}} is viewing.',
27011                                        '2': '{{person1}} and {{person2}} are viewing.',
27012                                        'one': '{{person1}}, {{person2}} and one other person are viewing.',
27013                                        'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27014                   </ng-pluralize>
27015                 </div>
27016               </file>
27017               <file name="protractor.js" type="protractor">
27018                 it('should show correct pluralized string', function() {
27019                   var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27020                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27021                   var countInput = element(by.model('personCount'));
27022
27023                   expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27024                   expect(withOffset.getText()).toEqual('Igor is viewing.');
27025
27026                   countInput.clear();
27027                   countInput.sendKeys('0');
27028
27029                   expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27030                   expect(withOffset.getText()).toEqual('Nobody is viewing.');
27031
27032                   countInput.clear();
27033                   countInput.sendKeys('2');
27034
27035                   expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27036                   expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27037
27038                   countInput.clear();
27039                   countInput.sendKeys('3');
27040
27041                   expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27042                   expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27043
27044                   countInput.clear();
27045                   countInput.sendKeys('4');
27046
27047                   expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27048                   expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27049                 });
27050                 it('should show data-bound names', function() {
27051                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27052                   var personCount = element(by.model('personCount'));
27053                   var person1 = element(by.model('person1'));
27054                   var person2 = element(by.model('person2'));
27055                   personCount.clear();
27056                   personCount.sendKeys('4');
27057                   person1.clear();
27058                   person1.sendKeys('Di');
27059                   person2.clear();
27060                   person2.sendKeys('Vojta');
27061                   expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27062                 });
27063               </file>
27064             </example>
27065          */
27066         var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27067           var BRACE = /{}/g,
27068               IS_WHEN = /^when(Minus)?(.+)$/;
27069
27070           return {
27071             link: function(scope, element, attr) {
27072               var numberExp = attr.count,
27073                   whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27074                   offset = attr.offset || 0,
27075                   whens = scope.$eval(whenExp) || {},
27076                   whensExpFns = {},
27077                   startSymbol = $interpolate.startSymbol(),
27078                   endSymbol = $interpolate.endSymbol(),
27079                   braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27080                   watchRemover = angular.noop,
27081                   lastCount;
27082
27083               forEach(attr, function(expression, attributeName) {
27084                 var tmpMatch = IS_WHEN.exec(attributeName);
27085                 if (tmpMatch) {
27086                   var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27087                   whens[whenKey] = element.attr(attr.$attr[attributeName]);
27088                 }
27089               });
27090               forEach(whens, function(expression, key) {
27091                 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27092
27093               });
27094
27095               scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27096                 var count = parseFloat(newVal);
27097                 var countIsNaN = isNaN(count);
27098
27099                 if (!countIsNaN && !(count in whens)) {
27100                   // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27101                   // Otherwise, check it against pluralization rules in $locale service.
27102                   count = $locale.pluralCat(count - offset);
27103                 }
27104
27105                 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27106                 // In JS `NaN !== NaN`, so we have to exlicitly check.
27107                 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27108                   watchRemover();
27109                   var whenExpFn = whensExpFns[count];
27110                   if (isUndefined(whenExpFn)) {
27111                     if (newVal != null) {
27112                       $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27113                     }
27114                     watchRemover = noop;
27115                     updateElementText();
27116                   } else {
27117                     watchRemover = scope.$watch(whenExpFn, updateElementText);
27118                   }
27119                   lastCount = count;
27120                 }
27121               });
27122
27123               function updateElementText(newText) {
27124                 element.text(newText || '');
27125               }
27126             }
27127           };
27128         }];
27129
27130         /**
27131          * @ngdoc directive
27132          * @name ngRepeat
27133          * @multiElement
27134          *
27135          * @description
27136          * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27137          * instance gets its own scope, where the given loop variable is set to the current collection item,
27138          * and `$index` is set to the item index or key.
27139          *
27140          * Special properties are exposed on the local scope of each template instance, including:
27141          *
27142          * | Variable  | Type            | Details                                                                     |
27143          * |-----------|-----------------|-----------------------------------------------------------------------------|
27144          * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27145          * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27146          * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27147          * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27148          * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27149          * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27150          *
27151          * <div class="alert alert-info">
27152          *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27153          *   This may be useful when, for instance, nesting ngRepeats.
27154          * </div>
27155          *
27156          *
27157          * # Iterating over object properties
27158          *
27159          * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27160          * syntax:
27161          *
27162          * ```js
27163          * <div ng-repeat="(key, value) in myObj"> ... </div>
27164          * ```
27165          *
27166          * You need to be aware that the JavaScript specification does not define the order of keys
27167          * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27168          * used to sort the keys alphabetically.)
27169          *
27170          * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27171          * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27172          * keys in the order in which they were defined, although there are exceptions when keys are deleted
27173          * 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).
27174          *
27175          * If this is not desired, the recommended workaround is to convert your object into an array
27176          * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27177          * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27178          * or implement a `$watch` on the object yourself.
27179          *
27180          *
27181          * # Tracking and Duplicates
27182          *
27183          * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27184          * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27185          *
27186          * * When an item is added, a new instance of the template is added to the DOM.
27187          * * When an item is removed, its template instance is removed from the DOM.
27188          * * When items are reordered, their respective templates are reordered in the DOM.
27189          *
27190          * To minimize creation of DOM elements, `ngRepeat` uses a function
27191          * to "keep track" of all items in the collection and their corresponding DOM elements.
27192          * For example, if an item is added to the collection, ngRepeat will know that all other items
27193          * already have DOM elements, and will not re-render them.
27194          *
27195          * The default tracking function (which tracks items by their identity) does not allow
27196          * duplicate items in arrays. This is because when there are duplicates, it is not possible
27197          * to maintain a one-to-one mapping between collection items and DOM elements.
27198          *
27199          * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27200          * with your own using the `track by` expression.
27201          *
27202          * For example, you may track items by the index of each item in the collection, using the
27203          * special scope property `$index`:
27204          * ```html
27205          *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27206          *      {{n}}
27207          *    </div>
27208          * ```
27209          *
27210          * You may also use arbitrary expressions in `track by`, including references to custom functions
27211          * on the scope:
27212          * ```html
27213          *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27214          *      {{n}}
27215          *    </div>
27216          * ```
27217          *
27218          * <div class="alert alert-success">
27219          * If you are working with objects that have an identifier property, you should track
27220          * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27221          * will not have to rebuild the DOM elements for items it has already rendered, even if the
27222          * JavaScript objects in the collection have been substituted for new ones. For large collections,
27223          * this signifincantly improves rendering performance. If you don't have a unique identifier,
27224          * `track by $index` can also provide a performance boost.
27225          * </div>
27226          * ```html
27227          *    <div ng-repeat="model in collection track by model.id">
27228          *      {{model.name}}
27229          *    </div>
27230          * ```
27231          *
27232          * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27233          * `$id` function, which tracks items by their identity:
27234          * ```html
27235          *    <div ng-repeat="obj in collection track by $id(obj)">
27236          *      {{obj.prop}}
27237          *    </div>
27238          * ```
27239          *
27240          * <div class="alert alert-warning">
27241          * **Note:** `track by` must always be the last expression:
27242          * </div>
27243          * ```
27244          * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27245          *     {{model.name}}
27246          * </div>
27247          * ```
27248          *
27249          * # Special repeat start and end points
27250          * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27251          * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27252          * 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)
27253          * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27254          *
27255          * The example below makes use of this feature:
27256          * ```html
27257          *   <header ng-repeat-start="item in items">
27258          *     Header {{ item }}
27259          *   </header>
27260          *   <div class="body">
27261          *     Body {{ item }}
27262          *   </div>
27263          *   <footer ng-repeat-end>
27264          *     Footer {{ item }}
27265          *   </footer>
27266          * ```
27267          *
27268          * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27269          * ```html
27270          *   <header>
27271          *     Header A
27272          *   </header>
27273          *   <div class="body">
27274          *     Body A
27275          *   </div>
27276          *   <footer>
27277          *     Footer A
27278          *   </footer>
27279          *   <header>
27280          *     Header B
27281          *   </header>
27282          *   <div class="body">
27283          *     Body B
27284          *   </div>
27285          *   <footer>
27286          *     Footer B
27287          *   </footer>
27288          * ```
27289          *
27290          * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27291          * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27292          *
27293          * @animations
27294          * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27295          *
27296          * **.leave** - when an item is removed from the list or when an item is filtered out
27297          *
27298          * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27299          *
27300          * @element ANY
27301          * @scope
27302          * @priority 1000
27303          * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27304          *   formats are currently supported:
27305          *
27306          *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27307          *     is a scope expression giving the collection to enumerate.
27308          *
27309          *     For example: `album in artist.albums`.
27310          *
27311          *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27312          *     and `expression` is the scope expression giving the collection to enumerate.
27313          *
27314          *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27315          *
27316          *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27317          *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27318          *     is specified, ng-repeat associates elements by identity. It is an error to have
27319          *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27320          *     mapped to the same DOM element, which is not possible.)
27321          *
27322          *     Note that the tracking expression must come last, after any filters, and the alias expression.
27323          *
27324          *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27325          *     will be associated by item identity in the array.
27326          *
27327          *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27328          *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27329          *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27330          *     element in the same way in the DOM.
27331          *
27332          *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27333          *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27334          *     property is same.
27335          *
27336          *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27337          *     to items in conjunction with a tracking expression.
27338          *
27339          *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27340          *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27341          *     when a filter is active on the repeater, but the filtered result set is empty.
27342          *
27343          *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27344          *     the items have been processed through the filter.
27345          *
27346          *     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
27347          *     (and not as operator, inside an expression).
27348          *
27349          *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27350          *
27351          * @example
27352          * This example initializes the scope to a list of names and
27353          * then uses `ngRepeat` to display every person:
27354           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27355             <file name="index.html">
27356               <div ng-init="friends = [
27357                 {name:'John', age:25, gender:'boy'},
27358                 {name:'Jessie', age:30, gender:'girl'},
27359                 {name:'Johanna', age:28, gender:'girl'},
27360                 {name:'Joy', age:15, gender:'girl'},
27361                 {name:'Mary', age:28, gender:'girl'},
27362                 {name:'Peter', age:95, gender:'boy'},
27363                 {name:'Sebastian', age:50, gender:'boy'},
27364                 {name:'Erika', age:27, gender:'girl'},
27365                 {name:'Patrick', age:40, gender:'boy'},
27366                 {name:'Samantha', age:60, gender:'girl'}
27367               ]">
27368                 I have {{friends.length}} friends. They are:
27369                 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27370                 <ul class="example-animate-container">
27371                   <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27372                     [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27373                   </li>
27374                   <li class="animate-repeat" ng-if="results.length == 0">
27375                     <strong>No results found...</strong>
27376                   </li>
27377                 </ul>
27378               </div>
27379             </file>
27380             <file name="animations.css">
27381               .example-animate-container {
27382                 background:white;
27383                 border:1px solid black;
27384                 list-style:none;
27385                 margin:0;
27386                 padding:0 10px;
27387               }
27388
27389               .animate-repeat {
27390                 line-height:40px;
27391                 list-style:none;
27392                 box-sizing:border-box;
27393               }
27394
27395               .animate-repeat.ng-move,
27396               .animate-repeat.ng-enter,
27397               .animate-repeat.ng-leave {
27398                 transition:all linear 0.5s;
27399               }
27400
27401               .animate-repeat.ng-leave.ng-leave-active,
27402               .animate-repeat.ng-move,
27403               .animate-repeat.ng-enter {
27404                 opacity:0;
27405                 max-height:0;
27406               }
27407
27408               .animate-repeat.ng-leave,
27409               .animate-repeat.ng-move.ng-move-active,
27410               .animate-repeat.ng-enter.ng-enter-active {
27411                 opacity:1;
27412                 max-height:40px;
27413               }
27414             </file>
27415             <file name="protractor.js" type="protractor">
27416               var friends = element.all(by.repeater('friend in friends'));
27417
27418               it('should render initial data set', function() {
27419                 expect(friends.count()).toBe(10);
27420                 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27421                 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27422                 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27423                 expect(element(by.binding('friends.length')).getText())
27424                     .toMatch("I have 10 friends. They are:");
27425               });
27426
27427                it('should update repeater when filter predicate changes', function() {
27428                  expect(friends.count()).toBe(10);
27429
27430                  element(by.model('q')).sendKeys('ma');
27431
27432                  expect(friends.count()).toBe(2);
27433                  expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27434                  expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27435                });
27436               </file>
27437             </example>
27438          */
27439         var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27440           var NG_REMOVED = '$$NG_REMOVED';
27441           var ngRepeatMinErr = minErr('ngRepeat');
27442
27443           var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27444             // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27445             scope[valueIdentifier] = value;
27446             if (keyIdentifier) scope[keyIdentifier] = key;
27447             scope.$index = index;
27448             scope.$first = (index === 0);
27449             scope.$last = (index === (arrayLength - 1));
27450             scope.$middle = !(scope.$first || scope.$last);
27451             // jshint bitwise: false
27452             scope.$odd = !(scope.$even = (index&1) === 0);
27453             // jshint bitwise: true
27454           };
27455
27456           var getBlockStart = function(block) {
27457             return block.clone[0];
27458           };
27459
27460           var getBlockEnd = function(block) {
27461             return block.clone[block.clone.length - 1];
27462           };
27463
27464
27465           return {
27466             restrict: 'A',
27467             multiElement: true,
27468             transclude: 'element',
27469             priority: 1000,
27470             terminal: true,
27471             $$tlb: true,
27472             compile: function ngRepeatCompile($element, $attr) {
27473               var expression = $attr.ngRepeat;
27474               var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27475
27476               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*$/);
27477
27478               if (!match) {
27479                 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27480                     expression);
27481               }
27482
27483               var lhs = match[1];
27484               var rhs = match[2];
27485               var aliasAs = match[3];
27486               var trackByExp = match[4];
27487
27488               match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27489
27490               if (!match) {
27491                 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27492                     lhs);
27493               }
27494               var valueIdentifier = match[3] || match[1];
27495               var keyIdentifier = match[2];
27496
27497               if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27498                   /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27499                 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27500                   aliasAs);
27501               }
27502
27503               var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27504               var hashFnLocals = {$id: hashKey};
27505
27506               if (trackByExp) {
27507                 trackByExpGetter = $parse(trackByExp);
27508               } else {
27509                 trackByIdArrayFn = function(key, value) {
27510                   return hashKey(value);
27511                 };
27512                 trackByIdObjFn = function(key) {
27513                   return key;
27514                 };
27515               }
27516
27517               return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27518
27519                 if (trackByExpGetter) {
27520                   trackByIdExpFn = function(key, value, index) {
27521                     // assign key, value, and $index to the locals so that they can be used in hash functions
27522                     if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27523                     hashFnLocals[valueIdentifier] = value;
27524                     hashFnLocals.$index = index;
27525                     return trackByExpGetter($scope, hashFnLocals);
27526                   };
27527                 }
27528
27529                 // Store a list of elements from previous run. This is a hash where key is the item from the
27530                 // iterator, and the value is objects with following properties.
27531                 //   - scope: bound scope
27532                 //   - element: previous element.
27533                 //   - index: position
27534                 //
27535                 // We are using no-proto object so that we don't need to guard against inherited props via
27536                 // hasOwnProperty.
27537                 var lastBlockMap = createMap();
27538
27539                 //watch props
27540                 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27541                   var index, length,
27542                       previousNode = $element[0],     // node that cloned nodes should be inserted after
27543                                                       // initialized to the comment node anchor
27544                       nextNode,
27545                       // Same as lastBlockMap but it has the current state. It will become the
27546                       // lastBlockMap on the next iteration.
27547                       nextBlockMap = createMap(),
27548                       collectionLength,
27549                       key, value, // key/value of iteration
27550                       trackById,
27551                       trackByIdFn,
27552                       collectionKeys,
27553                       block,       // last object information {scope, element, id}
27554                       nextBlockOrder,
27555                       elementsToRemove;
27556
27557                   if (aliasAs) {
27558                     $scope[aliasAs] = collection;
27559                   }
27560
27561                   if (isArrayLike(collection)) {
27562                     collectionKeys = collection;
27563                     trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27564                   } else {
27565                     trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27566                     // if object, extract keys, in enumeration order, unsorted
27567                     collectionKeys = [];
27568                     for (var itemKey in collection) {
27569                       if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27570                         collectionKeys.push(itemKey);
27571                       }
27572                     }
27573                   }
27574
27575                   collectionLength = collectionKeys.length;
27576                   nextBlockOrder = new Array(collectionLength);
27577
27578                   // locate existing items
27579                   for (index = 0; index < collectionLength; index++) {
27580                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27581                     value = collection[key];
27582                     trackById = trackByIdFn(key, value, index);
27583                     if (lastBlockMap[trackById]) {
27584                       // found previously seen block
27585                       block = lastBlockMap[trackById];
27586                       delete lastBlockMap[trackById];
27587                       nextBlockMap[trackById] = block;
27588                       nextBlockOrder[index] = block;
27589                     } else if (nextBlockMap[trackById]) {
27590                       // if collision detected. restore lastBlockMap and throw an error
27591                       forEach(nextBlockOrder, function(block) {
27592                         if (block && block.scope) lastBlockMap[block.id] = block;
27593                       });
27594                       throw ngRepeatMinErr('dupes',
27595                           "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27596                           expression, trackById, value);
27597                     } else {
27598                       // new never before seen block
27599                       nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27600                       nextBlockMap[trackById] = true;
27601                     }
27602                   }
27603
27604                   // remove leftover items
27605                   for (var blockKey in lastBlockMap) {
27606                     block = lastBlockMap[blockKey];
27607                     elementsToRemove = getBlockNodes(block.clone);
27608                     $animate.leave(elementsToRemove);
27609                     if (elementsToRemove[0].parentNode) {
27610                       // if the element was not removed yet because of pending animation, mark it as deleted
27611                       // so that we can ignore it later
27612                       for (index = 0, length = elementsToRemove.length; index < length; index++) {
27613                         elementsToRemove[index][NG_REMOVED] = true;
27614                       }
27615                     }
27616                     block.scope.$destroy();
27617                   }
27618
27619                   // we are not using forEach for perf reasons (trying to avoid #call)
27620                   for (index = 0; index < collectionLength; index++) {
27621                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27622                     value = collection[key];
27623                     block = nextBlockOrder[index];
27624
27625                     if (block.scope) {
27626                       // if we have already seen this object, then we need to reuse the
27627                       // associated scope/element
27628
27629                       nextNode = previousNode;
27630
27631                       // skip nodes that are already pending removal via leave animation
27632                       do {
27633                         nextNode = nextNode.nextSibling;
27634                       } while (nextNode && nextNode[NG_REMOVED]);
27635
27636                       if (getBlockStart(block) != nextNode) {
27637                         // existing item which got moved
27638                         $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27639                       }
27640                       previousNode = getBlockEnd(block);
27641                       updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27642                     } else {
27643                       // new item which we don't know about
27644                       $transclude(function ngRepeatTransclude(clone, scope) {
27645                         block.scope = scope;
27646                         // http://jsperf.com/clone-vs-createcomment
27647                         var endNode = ngRepeatEndComment.cloneNode(false);
27648                         clone[clone.length++] = endNode;
27649
27650                         // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27651                         $animate.enter(clone, null, jqLite(previousNode));
27652                         previousNode = endNode;
27653                         // Note: We only need the first/last node of the cloned nodes.
27654                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27655                         // by a directive with templateUrl when its template arrives.
27656                         block.clone = clone;
27657                         nextBlockMap[block.id] = block;
27658                         updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27659                       });
27660                     }
27661                   }
27662                   lastBlockMap = nextBlockMap;
27663                 });
27664               };
27665             }
27666           };
27667         }];
27668
27669         var NG_HIDE_CLASS = 'ng-hide';
27670         var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27671         /**
27672          * @ngdoc directive
27673          * @name ngShow
27674          * @multiElement
27675          *
27676          * @description
27677          * The `ngShow` directive shows or hides the given HTML element based on the expression
27678          * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27679          * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27680          * in AngularJS and sets the display style to none (using an !important flag).
27681          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27682          *
27683          * ```html
27684          * <!-- when $scope.myValue is truthy (element is visible) -->
27685          * <div ng-show="myValue"></div>
27686          *
27687          * <!-- when $scope.myValue is falsy (element is hidden) -->
27688          * <div ng-show="myValue" class="ng-hide"></div>
27689          * ```
27690          *
27691          * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27692          * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27693          * from the element causing the element not to appear hidden.
27694          *
27695          * ## Why is !important used?
27696          *
27697          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27698          * can be easily overridden by heavier selectors. For example, something as simple
27699          * as changing the display style on a HTML list item would make hidden elements appear visible.
27700          * This also becomes a bigger issue when dealing with CSS frameworks.
27701          *
27702          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27703          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27704          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27705          *
27706          * ### Overriding `.ng-hide`
27707          *
27708          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27709          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27710          * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27711          * with extra animation classes that can be added.
27712          *
27713          * ```css
27714          * .ng-hide:not(.ng-hide-animate) {
27715          *   /&#42; this is just another form of hiding an element &#42;/
27716          *   display: block!important;
27717          *   position: absolute;
27718          *   top: -9999px;
27719          *   left: -9999px;
27720          * }
27721          * ```
27722          *
27723          * By default you don't need to override in CSS anything and the animations will work around the display style.
27724          *
27725          * ## A note about animations with `ngShow`
27726          *
27727          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27728          * is true and false. This system works like the animation system present with ngClass except that
27729          * you must also include the !important flag to override the display property
27730          * so that you can perform an animation when the element is hidden during the time of the animation.
27731          *
27732          * ```css
27733          * //
27734          * //a working example can be found at the bottom of this page
27735          * //
27736          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27737          *   /&#42; this is required as of 1.3x to properly
27738          *      apply all styling in a show/hide animation &#42;/
27739          *   transition: 0s linear all;
27740          * }
27741          *
27742          * .my-element.ng-hide-add-active,
27743          * .my-element.ng-hide-remove-active {
27744          *   /&#42; the transition is defined in the active class &#42;/
27745          *   transition: 1s linear all;
27746          * }
27747          *
27748          * .my-element.ng-hide-add { ... }
27749          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27750          * .my-element.ng-hide-remove { ... }
27751          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27752          * ```
27753          *
27754          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27755          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27756          *
27757          * @animations
27758          * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27759          * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27760          *
27761          * @element ANY
27762          * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27763          *     then the element is shown or hidden respectively.
27764          *
27765          * @example
27766           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27767             <file name="index.html">
27768               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27769               <div>
27770                 Show:
27771                 <div class="check-element animate-show" ng-show="checked">
27772                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27773                 </div>
27774               </div>
27775               <div>
27776                 Hide:
27777                 <div class="check-element animate-show" ng-hide="checked">
27778                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27779                 </div>
27780               </div>
27781             </file>
27782             <file name="glyphicons.css">
27783               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27784             </file>
27785             <file name="animations.css">
27786               .animate-show {
27787                 line-height: 20px;
27788                 opacity: 1;
27789                 padding: 10px;
27790                 border: 1px solid black;
27791                 background: white;
27792               }
27793
27794               .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27795                 transition: all linear 0.5s;
27796               }
27797
27798               .animate-show.ng-hide {
27799                 line-height: 0;
27800                 opacity: 0;
27801                 padding: 0 10px;
27802               }
27803
27804               .check-element {
27805                 padding: 10px;
27806                 border: 1px solid black;
27807                 background: white;
27808               }
27809             </file>
27810             <file name="protractor.js" type="protractor">
27811               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27812               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27813
27814               it('should check ng-show / ng-hide', function() {
27815                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27816                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27817
27818                 element(by.model('checked')).click();
27819
27820                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27821                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27822               });
27823             </file>
27824           </example>
27825          */
27826         var ngShowDirective = ['$animate', function($animate) {
27827           return {
27828             restrict: 'A',
27829             multiElement: true,
27830             link: function(scope, element, attr) {
27831               scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27832                 // we're adding a temporary, animation-specific class for ng-hide since this way
27833                 // we can control when the element is actually displayed on screen without having
27834                 // to have a global/greedy CSS selector that breaks when other animations are run.
27835                 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27836                 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27837                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27838                 });
27839               });
27840             }
27841           };
27842         }];
27843
27844
27845         /**
27846          * @ngdoc directive
27847          * @name ngHide
27848          * @multiElement
27849          *
27850          * @description
27851          * The `ngHide` directive shows or hides the given HTML element based on the expression
27852          * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27853          * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27854          * in AngularJS and sets the display style to none (using an !important flag).
27855          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27856          *
27857          * ```html
27858          * <!-- when $scope.myValue is truthy (element is hidden) -->
27859          * <div ng-hide="myValue" class="ng-hide"></div>
27860          *
27861          * <!-- when $scope.myValue is falsy (element is visible) -->
27862          * <div ng-hide="myValue"></div>
27863          * ```
27864          *
27865          * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27866          * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27867          * from the element causing the element not to appear hidden.
27868          *
27869          * ## Why is !important used?
27870          *
27871          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27872          * can be easily overridden by heavier selectors. For example, something as simple
27873          * as changing the display style on a HTML list item would make hidden elements appear visible.
27874          * This also becomes a bigger issue when dealing with CSS frameworks.
27875          *
27876          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27877          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27878          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27879          *
27880          * ### Overriding `.ng-hide`
27881          *
27882          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27883          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27884          * class in CSS:
27885          *
27886          * ```css
27887          * .ng-hide {
27888          *   /&#42; this is just another form of hiding an element &#42;/
27889          *   display: block!important;
27890          *   position: absolute;
27891          *   top: -9999px;
27892          *   left: -9999px;
27893          * }
27894          * ```
27895          *
27896          * By default you don't need to override in CSS anything and the animations will work around the display style.
27897          *
27898          * ## A note about animations with `ngHide`
27899          *
27900          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27901          * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27902          * CSS class is added and removed for you instead of your own CSS class.
27903          *
27904          * ```css
27905          * //
27906          * //a working example can be found at the bottom of this page
27907          * //
27908          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27909          *   transition: 0.5s linear all;
27910          * }
27911          *
27912          * .my-element.ng-hide-add { ... }
27913          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27914          * .my-element.ng-hide-remove { ... }
27915          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27916          * ```
27917          *
27918          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27919          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27920          *
27921          * @animations
27922          * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27923          * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27924          *
27925          * @element ANY
27926          * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27927          *     the element is shown or hidden respectively.
27928          *
27929          * @example
27930           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27931             <file name="index.html">
27932               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27933               <div>
27934                 Show:
27935                 <div class="check-element animate-hide" ng-show="checked">
27936                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27937                 </div>
27938               </div>
27939               <div>
27940                 Hide:
27941                 <div class="check-element animate-hide" ng-hide="checked">
27942                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27943                 </div>
27944               </div>
27945             </file>
27946             <file name="glyphicons.css">
27947               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27948             </file>
27949             <file name="animations.css">
27950               .animate-hide {
27951                 transition: all linear 0.5s;
27952                 line-height: 20px;
27953                 opacity: 1;
27954                 padding: 10px;
27955                 border: 1px solid black;
27956                 background: white;
27957               }
27958
27959               .animate-hide.ng-hide {
27960                 line-height: 0;
27961                 opacity: 0;
27962                 padding: 0 10px;
27963               }
27964
27965               .check-element {
27966                 padding: 10px;
27967                 border: 1px solid black;
27968                 background: white;
27969               }
27970             </file>
27971             <file name="protractor.js" type="protractor">
27972               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27973               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27974
27975               it('should check ng-show / ng-hide', function() {
27976                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27977                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27978
27979                 element(by.model('checked')).click();
27980
27981                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27982                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27983               });
27984             </file>
27985           </example>
27986          */
27987         var ngHideDirective = ['$animate', function($animate) {
27988           return {
27989             restrict: 'A',
27990             multiElement: true,
27991             link: function(scope, element, attr) {
27992               scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27993                 // The comment inside of the ngShowDirective explains why we add and
27994                 // remove a temporary class for the show/hide animation
27995                 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27996                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27997                 });
27998               });
27999             }
28000           };
28001         }];
28002
28003         /**
28004          * @ngdoc directive
28005          * @name ngStyle
28006          * @restrict AC
28007          *
28008          * @description
28009          * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28010          *
28011          * @element ANY
28012          * @param {expression} ngStyle
28013          *
28014          * {@link guide/expression Expression} which evals to an
28015          * object whose keys are CSS style names and values are corresponding values for those CSS
28016          * keys.
28017          *
28018          * Since some CSS style names are not valid keys for an object, they must be quoted.
28019          * See the 'background-color' style in the example below.
28020          *
28021          * @example
28022            <example>
28023              <file name="index.html">
28024                 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28025                 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28026                 <input type="button" value="clear" ng-click="myStyle={}">
28027                 <br/>
28028                 <span ng-style="myStyle">Sample Text</span>
28029                 <pre>myStyle={{myStyle}}</pre>
28030              </file>
28031              <file name="style.css">
28032                span {
28033                  color: black;
28034                }
28035              </file>
28036              <file name="protractor.js" type="protractor">
28037                var colorSpan = element(by.css('span'));
28038
28039                it('should check ng-style', function() {
28040                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28041                  element(by.css('input[value=\'set color\']')).click();
28042                  expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28043                  element(by.css('input[value=clear]')).click();
28044                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28045                });
28046              </file>
28047            </example>
28048          */
28049         var ngStyleDirective = ngDirective(function(scope, element, attr) {
28050           scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28051             if (oldStyles && (newStyles !== oldStyles)) {
28052               forEach(oldStyles, function(val, style) { element.css(style, '');});
28053             }
28054             if (newStyles) element.css(newStyles);
28055           }, true);
28056         });
28057
28058         /**
28059          * @ngdoc directive
28060          * @name ngSwitch
28061          * @restrict EA
28062          *
28063          * @description
28064          * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28065          * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28066          * as specified in the template.
28067          *
28068          * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28069          * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28070          * matches the value obtained from the evaluated expression. In other words, you define a container element
28071          * (where you place the directive), place an expression on the **`on="..."` attribute**
28072          * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28073          * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28074          * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28075          * attribute is displayed.
28076          *
28077          * <div class="alert alert-info">
28078          * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28079          * as literal string values to match against.
28080          * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28081          * value of the expression `$scope.someVal`.
28082          * </div>
28083
28084          * @animations
28085          * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28086          * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28087          *
28088          * @usage
28089          *
28090          * ```
28091          * <ANY ng-switch="expression">
28092          *   <ANY ng-switch-when="matchValue1">...</ANY>
28093          *   <ANY ng-switch-when="matchValue2">...</ANY>
28094          *   <ANY ng-switch-default>...</ANY>
28095          * </ANY>
28096          * ```
28097          *
28098          *
28099          * @scope
28100          * @priority 1200
28101          * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28102          * On child elements add:
28103          *
28104          * * `ngSwitchWhen`: the case statement to match against. If match then this
28105          *   case will be displayed. If the same match appears multiple times, all the
28106          *   elements will be displayed.
28107          * * `ngSwitchDefault`: the default case when no other case match. If there
28108          *   are multiple default cases, all of them will be displayed when no other
28109          *   case match.
28110          *
28111          *
28112          * @example
28113           <example module="switchExample" deps="angular-animate.js" animations="true">
28114             <file name="index.html">
28115               <div ng-controller="ExampleController">
28116                 <select ng-model="selection" ng-options="item for item in items">
28117                 </select>
28118                 <code>selection={{selection}}</code>
28119                 <hr/>
28120                 <div class="animate-switch-container"
28121                   ng-switch on="selection">
28122                     <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28123                     <div class="animate-switch" ng-switch-when="home">Home Span</div>
28124                     <div class="animate-switch" ng-switch-default>default</div>
28125                 </div>
28126               </div>
28127             </file>
28128             <file name="script.js">
28129               angular.module('switchExample', ['ngAnimate'])
28130                 .controller('ExampleController', ['$scope', function($scope) {
28131                   $scope.items = ['settings', 'home', 'other'];
28132                   $scope.selection = $scope.items[0];
28133                 }]);
28134             </file>
28135             <file name="animations.css">
28136               .animate-switch-container {
28137                 position:relative;
28138                 background:white;
28139                 border:1px solid black;
28140                 height:40px;
28141                 overflow:hidden;
28142               }
28143
28144               .animate-switch {
28145                 padding:10px;
28146               }
28147
28148               .animate-switch.ng-animate {
28149                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28150
28151                 position:absolute;
28152                 top:0;
28153                 left:0;
28154                 right:0;
28155                 bottom:0;
28156               }
28157
28158               .animate-switch.ng-leave.ng-leave-active,
28159               .animate-switch.ng-enter {
28160                 top:-50px;
28161               }
28162               .animate-switch.ng-leave,
28163               .animate-switch.ng-enter.ng-enter-active {
28164                 top:0;
28165               }
28166             </file>
28167             <file name="protractor.js" type="protractor">
28168               var switchElem = element(by.css('[ng-switch]'));
28169               var select = element(by.model('selection'));
28170
28171               it('should start in settings', function() {
28172                 expect(switchElem.getText()).toMatch(/Settings Div/);
28173               });
28174               it('should change to home', function() {
28175                 select.all(by.css('option')).get(1).click();
28176                 expect(switchElem.getText()).toMatch(/Home Span/);
28177               });
28178               it('should select default', function() {
28179                 select.all(by.css('option')).get(2).click();
28180                 expect(switchElem.getText()).toMatch(/default/);
28181               });
28182             </file>
28183           </example>
28184          */
28185         var ngSwitchDirective = ['$animate', function($animate) {
28186           return {
28187             require: 'ngSwitch',
28188
28189             // asks for $scope to fool the BC controller module
28190             controller: ['$scope', function ngSwitchController() {
28191              this.cases = {};
28192             }],
28193             link: function(scope, element, attr, ngSwitchController) {
28194               var watchExpr = attr.ngSwitch || attr.on,
28195                   selectedTranscludes = [],
28196                   selectedElements = [],
28197                   previousLeaveAnimations = [],
28198                   selectedScopes = [];
28199
28200               var spliceFactory = function(array, index) {
28201                   return function() { array.splice(index, 1); };
28202               };
28203
28204               scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28205                 var i, ii;
28206                 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28207                   $animate.cancel(previousLeaveAnimations[i]);
28208                 }
28209                 previousLeaveAnimations.length = 0;
28210
28211                 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28212                   var selected = getBlockNodes(selectedElements[i].clone);
28213                   selectedScopes[i].$destroy();
28214                   var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28215                   promise.then(spliceFactory(previousLeaveAnimations, i));
28216                 }
28217
28218                 selectedElements.length = 0;
28219                 selectedScopes.length = 0;
28220
28221                 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28222                   forEach(selectedTranscludes, function(selectedTransclude) {
28223                     selectedTransclude.transclude(function(caseElement, selectedScope) {
28224                       selectedScopes.push(selectedScope);
28225                       var anchor = selectedTransclude.element;
28226                       caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28227                       var block = { clone: caseElement };
28228
28229                       selectedElements.push(block);
28230                       $animate.enter(caseElement, anchor.parent(), anchor);
28231                     });
28232                   });
28233                 }
28234               });
28235             }
28236           };
28237         }];
28238
28239         var ngSwitchWhenDirective = ngDirective({
28240           transclude: 'element',
28241           priority: 1200,
28242           require: '^ngSwitch',
28243           multiElement: true,
28244           link: function(scope, element, attrs, ctrl, $transclude) {
28245             ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28246             ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28247           }
28248         });
28249
28250         var ngSwitchDefaultDirective = ngDirective({
28251           transclude: 'element',
28252           priority: 1200,
28253           require: '^ngSwitch',
28254           multiElement: true,
28255           link: function(scope, element, attr, ctrl, $transclude) {
28256             ctrl.cases['?'] = (ctrl.cases['?'] || []);
28257             ctrl.cases['?'].push({ transclude: $transclude, element: element });
28258            }
28259         });
28260
28261         /**
28262          * @ngdoc directive
28263          * @name ngTransclude
28264          * @restrict EAC
28265          *
28266          * @description
28267          * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28268          *
28269          * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28270          *
28271          * @element ANY
28272          *
28273          * @example
28274            <example module="transcludeExample">
28275              <file name="index.html">
28276                <script>
28277                  angular.module('transcludeExample', [])
28278                   .directive('pane', function(){
28279                      return {
28280                        restrict: 'E',
28281                        transclude: true,
28282                        scope: { title:'@' },
28283                        template: '<div style="border: 1px solid black;">' +
28284                                    '<div style="background-color: gray">{{title}}</div>' +
28285                                    '<ng-transclude></ng-transclude>' +
28286                                  '</div>'
28287                      };
28288                  })
28289                  .controller('ExampleController', ['$scope', function($scope) {
28290                    $scope.title = 'Lorem Ipsum';
28291                    $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28292                  }]);
28293                </script>
28294                <div ng-controller="ExampleController">
28295                  <input ng-model="title" aria-label="title"> <br/>
28296                  <textarea ng-model="text" aria-label="text"></textarea> <br/>
28297                  <pane title="{{title}}">{{text}}</pane>
28298                </div>
28299              </file>
28300              <file name="protractor.js" type="protractor">
28301                 it('should have transcluded', function() {
28302                   var titleElement = element(by.model('title'));
28303                   titleElement.clear();
28304                   titleElement.sendKeys('TITLE');
28305                   var textElement = element(by.model('text'));
28306                   textElement.clear();
28307                   textElement.sendKeys('TEXT');
28308                   expect(element(by.binding('title')).getText()).toEqual('TITLE');
28309                   expect(element(by.binding('text')).getText()).toEqual('TEXT');
28310                 });
28311              </file>
28312            </example>
28313          *
28314          */
28315         var ngTranscludeDirective = ngDirective({
28316           restrict: 'EAC',
28317           link: function($scope, $element, $attrs, controller, $transclude) {
28318             if (!$transclude) {
28319               throw minErr('ngTransclude')('orphan',
28320                'Illegal use of ngTransclude directive in the template! ' +
28321                'No parent directive that requires a transclusion found. ' +
28322                'Element: {0}',
28323                startingTag($element));
28324             }
28325
28326             $transclude(function(clone) {
28327               $element.empty();
28328               $element.append(clone);
28329             });
28330           }
28331         });
28332
28333         /**
28334          * @ngdoc directive
28335          * @name script
28336          * @restrict E
28337          *
28338          * @description
28339          * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28340          * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28341          * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28342          * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28343          * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28344          *
28345          * @param {string} type Must be set to `'text/ng-template'`.
28346          * @param {string} id Cache name of the template.
28347          *
28348          * @example
28349           <example>
28350             <file name="index.html">
28351               <script type="text/ng-template" id="/tpl.html">
28352                 Content of the template.
28353               </script>
28354
28355               <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28356               <div id="tpl-content" ng-include src="currentTpl"></div>
28357             </file>
28358             <file name="protractor.js" type="protractor">
28359               it('should load template defined inside script tag', function() {
28360                 element(by.css('#tpl-link')).click();
28361                 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28362               });
28363             </file>
28364           </example>
28365          */
28366         var scriptDirective = ['$templateCache', function($templateCache) {
28367           return {
28368             restrict: 'E',
28369             terminal: true,
28370             compile: function(element, attr) {
28371               if (attr.type == 'text/ng-template') {
28372                 var templateUrl = attr.id,
28373                     text = element[0].text;
28374
28375                 $templateCache.put(templateUrl, text);
28376               }
28377             }
28378           };
28379         }];
28380
28381         var noopNgModelController = { $setViewValue: noop, $render: noop };
28382
28383         function chromeHack(optionElement) {
28384           // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28385           // Adding an <option selected="selected"> element to a <select required="required"> should
28386           // automatically select the new element
28387           if (optionElement[0].hasAttribute('selected')) {
28388             optionElement[0].selected = true;
28389           }
28390         }
28391
28392         /**
28393          * @ngdoc type
28394          * @name  select.SelectController
28395          * @description
28396          * The controller for the `<select>` directive. This provides support for reading
28397          * and writing the selected value(s) of the control and also coordinates dynamically
28398          * added `<option>` elements, perhaps by an `ngRepeat` directive.
28399          */
28400         var SelectController =
28401                 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28402
28403           var self = this,
28404               optionsMap = new HashMap();
28405
28406           // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28407           self.ngModelCtrl = noopNgModelController;
28408
28409           // The "unknown" option is one that is prepended to the list if the viewValue
28410           // does not match any of the options. When it is rendered the value of the unknown
28411           // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28412           //
28413           // We can't just jqLite('<option>') since jqLite is not smart enough
28414           // to create it in <select> and IE barfs otherwise.
28415           self.unknownOption = jqLite(document.createElement('option'));
28416           self.renderUnknownOption = function(val) {
28417             var unknownVal = '? ' + hashKey(val) + ' ?';
28418             self.unknownOption.val(unknownVal);
28419             $element.prepend(self.unknownOption);
28420             $element.val(unknownVal);
28421           };
28422
28423           $scope.$on('$destroy', function() {
28424             // disable unknown option so that we don't do work when the whole select is being destroyed
28425             self.renderUnknownOption = noop;
28426           });
28427
28428           self.removeUnknownOption = function() {
28429             if (self.unknownOption.parent()) self.unknownOption.remove();
28430           };
28431
28432
28433           // Read the value of the select control, the implementation of this changes depending
28434           // upon whether the select can have multiple values and whether ngOptions is at work.
28435           self.readValue = function readSingleValue() {
28436             self.removeUnknownOption();
28437             return $element.val();
28438           };
28439
28440
28441           // Write the value to the select control, the implementation of this changes depending
28442           // upon whether the select can have multiple values and whether ngOptions is at work.
28443           self.writeValue = function writeSingleValue(value) {
28444             if (self.hasOption(value)) {
28445               self.removeUnknownOption();
28446               $element.val(value);
28447               if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28448             } else {
28449               if (value == null && self.emptyOption) {
28450                 self.removeUnknownOption();
28451                 $element.val('');
28452               } else {
28453                 self.renderUnknownOption(value);
28454               }
28455             }
28456           };
28457
28458
28459           // Tell the select control that an option, with the given value, has been added
28460           self.addOption = function(value, element) {
28461             assertNotHasOwnProperty(value, '"option value"');
28462             if (value === '') {
28463               self.emptyOption = element;
28464             }
28465             var count = optionsMap.get(value) || 0;
28466             optionsMap.put(value, count + 1);
28467             self.ngModelCtrl.$render();
28468             chromeHack(element);
28469           };
28470
28471           // Tell the select control that an option, with the given value, has been removed
28472           self.removeOption = function(value) {
28473             var count = optionsMap.get(value);
28474             if (count) {
28475               if (count === 1) {
28476                 optionsMap.remove(value);
28477                 if (value === '') {
28478                   self.emptyOption = undefined;
28479                 }
28480               } else {
28481                 optionsMap.put(value, count - 1);
28482               }
28483             }
28484           };
28485
28486           // Check whether the select control has an option matching the given value
28487           self.hasOption = function(value) {
28488             return !!optionsMap.get(value);
28489           };
28490
28491
28492           self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28493
28494             if (interpolateValueFn) {
28495               // The value attribute is interpolated
28496               var oldVal;
28497               optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28498                 if (isDefined(oldVal)) {
28499                   self.removeOption(oldVal);
28500                 }
28501                 oldVal = newVal;
28502                 self.addOption(newVal, optionElement);
28503               });
28504             } else if (interpolateTextFn) {
28505               // The text content is interpolated
28506               optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28507                 optionAttrs.$set('value', newVal);
28508                 if (oldVal !== newVal) {
28509                   self.removeOption(oldVal);
28510                 }
28511                 self.addOption(newVal, optionElement);
28512               });
28513             } else {
28514               // The value attribute is static
28515               self.addOption(optionAttrs.value, optionElement);
28516             }
28517
28518             optionElement.on('$destroy', function() {
28519               self.removeOption(optionAttrs.value);
28520               self.ngModelCtrl.$render();
28521             });
28522           };
28523         }];
28524
28525         /**
28526          * @ngdoc directive
28527          * @name select
28528          * @restrict E
28529          *
28530          * @description
28531          * HTML `SELECT` element with angular data-binding.
28532          *
28533          * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28534          * between the scope and the `<select>` control (including setting default values).
28535          * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28536          * {@link ngOptions `ngOptions`} directives.
28537          *
28538          * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28539          * to the model identified by the `ngModel` directive. With static or repeated options, this is
28540          * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28541          * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28542          *
28543          * <div class="alert alert-warning">
28544          * Note that the value of a `select` directive used without `ngOptions` is always a string.
28545          * When the model needs to be bound to a non-string value, you must either explictly convert it
28546          * using a directive (see example below) or use `ngOptions` to specify the set of options.
28547          * This is because an option element can only be bound to string values at present.
28548          * </div>
28549          *
28550          * If the viewValue of `ngModel` does not match any of the options, then the control
28551          * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28552          *
28553          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28554          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28555          * option. See example below for demonstration.
28556          *
28557          * <div class="alert alert-info">
28558          * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28559          * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28560          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28561          * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28562          * a new scope for each repeated instance.
28563          * </div>
28564          *
28565          *
28566          * @param {string} ngModel Assignable angular expression to data-bind to.
28567          * @param {string=} name Property name of the form under which the control is published.
28568          * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28569          *     bound to the model as an array.
28570          * @param {string=} required Sets `required` validation error key if the value is not entered.
28571          * @param {string=} ngRequired Adds required attribute and required validation constraint to
28572          * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28573          * when you want to data-bind to the required attribute.
28574          * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28575          *    interaction with the select element.
28576          * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28577          * set on the model on selection. See {@link ngOptions `ngOptions`}.
28578          *
28579          * @example
28580          * ### Simple `select` elements with static options
28581          *
28582          * <example name="static-select" module="staticSelect">
28583          * <file name="index.html">
28584          * <div ng-controller="ExampleController">
28585          *   <form name="myForm">
28586          *     <label for="singleSelect"> Single select: </label><br>
28587          *     <select name="singleSelect" ng-model="data.singleSelect">
28588          *       <option value="option-1">Option 1</option>
28589          *       <option value="option-2">Option 2</option>
28590          *     </select><br>
28591          *
28592          *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28593          *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28594          *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28595          *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28596          *       <option value="option-2">Option 2</option>
28597          *     </select><br>
28598          *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28599          *     <tt>singleSelect = {{data.singleSelect}}</tt>
28600          *
28601          *     <hr>
28602          *     <label for="multipleSelect"> Multiple select: </label><br>
28603          *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28604          *       <option value="option-1">Option 1</option>
28605          *       <option value="option-2">Option 2</option>
28606          *       <option value="option-3">Option 3</option>
28607          *     </select><br>
28608          *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28609          *   </form>
28610          * </div>
28611          * </file>
28612          * <file name="app.js">
28613          *  angular.module('staticSelect', [])
28614          *    .controller('ExampleController', ['$scope', function($scope) {
28615          *      $scope.data = {
28616          *       singleSelect: null,
28617          *       multipleSelect: [],
28618          *       option1: 'option-1',
28619          *      };
28620          *
28621          *      $scope.forceUnknownOption = function() {
28622          *        $scope.data.singleSelect = 'nonsense';
28623          *      };
28624          *   }]);
28625          * </file>
28626          *</example>
28627          *
28628          * ### Using `ngRepeat` to generate `select` options
28629          * <example name="ngrepeat-select" module="ngrepeatSelect">
28630          * <file name="index.html">
28631          * <div ng-controller="ExampleController">
28632          *   <form name="myForm">
28633          *     <label for="repeatSelect"> Repeat select: </label>
28634          *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28635          *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28636          *     </select>
28637          *   </form>
28638          *   <hr>
28639          *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28640          * </div>
28641          * </file>
28642          * <file name="app.js">
28643          *  angular.module('ngrepeatSelect', [])
28644          *    .controller('ExampleController', ['$scope', function($scope) {
28645          *      $scope.data = {
28646          *       repeatSelect: null,
28647          *       availableOptions: [
28648          *         {id: '1', name: 'Option A'},
28649          *         {id: '2', name: 'Option B'},
28650          *         {id: '3', name: 'Option C'}
28651          *       ],
28652          *      };
28653          *   }]);
28654          * </file>
28655          *</example>
28656          *
28657          *
28658          * ### Using `select` with `ngOptions` and setting a default value
28659          * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28660          *
28661          * <example name="select-with-default-values" module="defaultValueSelect">
28662          * <file name="index.html">
28663          * <div ng-controller="ExampleController">
28664          *   <form name="myForm">
28665          *     <label for="mySelect">Make a choice:</label>
28666          *     <select name="mySelect" id="mySelect"
28667          *       ng-options="option.name for option in data.availableOptions track by option.id"
28668          *       ng-model="data.selectedOption"></select>
28669          *   </form>
28670          *   <hr>
28671          *   <tt>option = {{data.selectedOption}}</tt><br/>
28672          * </div>
28673          * </file>
28674          * <file name="app.js">
28675          *  angular.module('defaultValueSelect', [])
28676          *    .controller('ExampleController', ['$scope', function($scope) {
28677          *      $scope.data = {
28678          *       availableOptions: [
28679          *         {id: '1', name: 'Option A'},
28680          *         {id: '2', name: 'Option B'},
28681          *         {id: '3', name: 'Option C'}
28682          *       ],
28683          *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28684          *       };
28685          *   }]);
28686          * </file>
28687          *</example>
28688          *
28689          *
28690          * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28691          *
28692          * <example name="select-with-non-string-options" module="nonStringSelect">
28693          *   <file name="index.html">
28694          *     <select ng-model="model.id" convert-to-number>
28695          *       <option value="0">Zero</option>
28696          *       <option value="1">One</option>
28697          *       <option value="2">Two</option>
28698          *     </select>
28699          *     {{ model }}
28700          *   </file>
28701          *   <file name="app.js">
28702          *     angular.module('nonStringSelect', [])
28703          *       .run(function($rootScope) {
28704          *         $rootScope.model = { id: 2 };
28705          *       })
28706          *       .directive('convertToNumber', function() {
28707          *         return {
28708          *           require: 'ngModel',
28709          *           link: function(scope, element, attrs, ngModel) {
28710          *             ngModel.$parsers.push(function(val) {
28711          *               return parseInt(val, 10);
28712          *             });
28713          *             ngModel.$formatters.push(function(val) {
28714          *               return '' + val;
28715          *             });
28716          *           }
28717          *         };
28718          *       });
28719          *   </file>
28720          *   <file name="protractor.js" type="protractor">
28721          *     it('should initialize to model', function() {
28722          *       var select = element(by.css('select'));
28723          *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28724          *     });
28725          *   </file>
28726          * </example>
28727          *
28728          */
28729         var selectDirective = function() {
28730
28731           return {
28732             restrict: 'E',
28733             require: ['select', '?ngModel'],
28734             controller: SelectController,
28735             priority: 1,
28736             link: {
28737               pre: selectPreLink
28738             }
28739           };
28740
28741           function selectPreLink(scope, element, attr, ctrls) {
28742
28743               // if ngModel is not defined, we don't need to do anything
28744               var ngModelCtrl = ctrls[1];
28745               if (!ngModelCtrl) return;
28746
28747               var selectCtrl = ctrls[0];
28748
28749               selectCtrl.ngModelCtrl = ngModelCtrl;
28750
28751               // We delegate rendering to the `writeValue` method, which can be changed
28752               // if the select can have multiple selected values or if the options are being
28753               // generated by `ngOptions`
28754               ngModelCtrl.$render = function() {
28755                 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28756               };
28757
28758               // When the selected item(s) changes we delegate getting the value of the select control
28759               // to the `readValue` method, which can be changed if the select can have multiple
28760               // selected values or if the options are being generated by `ngOptions`
28761               element.on('change', function() {
28762                 scope.$apply(function() {
28763                   ngModelCtrl.$setViewValue(selectCtrl.readValue());
28764                 });
28765               });
28766
28767               // If the select allows multiple values then we need to modify how we read and write
28768               // values from and to the control; also what it means for the value to be empty and
28769               // we have to add an extra watch since ngModel doesn't work well with arrays - it
28770               // doesn't trigger rendering if only an item in the array changes.
28771               if (attr.multiple) {
28772
28773                 // Read value now needs to check each option to see if it is selected
28774                 selectCtrl.readValue = function readMultipleValue() {
28775                   var array = [];
28776                   forEach(element.find('option'), function(option) {
28777                     if (option.selected) {
28778                       array.push(option.value);
28779                     }
28780                   });
28781                   return array;
28782                 };
28783
28784                 // Write value now needs to set the selected property of each matching option
28785                 selectCtrl.writeValue = function writeMultipleValue(value) {
28786                   var items = new HashMap(value);
28787                   forEach(element.find('option'), function(option) {
28788                     option.selected = isDefined(items.get(option.value));
28789                   });
28790                 };
28791
28792                 // we have to do it on each watch since ngModel watches reference, but
28793                 // we need to work of an array, so we need to see if anything was inserted/removed
28794                 var lastView, lastViewRef = NaN;
28795                 scope.$watch(function selectMultipleWatch() {
28796                   if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28797                     lastView = shallowCopy(ngModelCtrl.$viewValue);
28798                     ngModelCtrl.$render();
28799                   }
28800                   lastViewRef = ngModelCtrl.$viewValue;
28801                 });
28802
28803                 // If we are a multiple select then value is now a collection
28804                 // so the meaning of $isEmpty changes
28805                 ngModelCtrl.$isEmpty = function(value) {
28806                   return !value || value.length === 0;
28807                 };
28808
28809               }
28810             }
28811         };
28812
28813
28814         // The option directive is purely designed to communicate the existence (or lack of)
28815         // of dynamically created (and destroyed) option elements to their containing select
28816         // directive via its controller.
28817         var optionDirective = ['$interpolate', function($interpolate) {
28818           return {
28819             restrict: 'E',
28820             priority: 100,
28821             compile: function(element, attr) {
28822
28823               if (isDefined(attr.value)) {
28824                 // If the value attribute is defined, check if it contains an interpolation
28825                 var interpolateValueFn = $interpolate(attr.value, true);
28826               } else {
28827                 // If the value attribute is not defined then we fall back to the
28828                 // text content of the option element, which may be interpolated
28829                 var interpolateTextFn = $interpolate(element.text(), true);
28830                 if (!interpolateTextFn) {
28831                   attr.$set('value', element.text());
28832                 }
28833               }
28834
28835               return function(scope, element, attr) {
28836
28837                 // This is an optimization over using ^^ since we don't want to have to search
28838                 // all the way to the root of the DOM for every single option element
28839                 var selectCtrlName = '$selectController',
28840                     parent = element.parent(),
28841                     selectCtrl = parent.data(selectCtrlName) ||
28842                       parent.parent().data(selectCtrlName); // in case we are in optgroup
28843
28844                 if (selectCtrl) {
28845                   selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28846                 }
28847               };
28848             }
28849           };
28850         }];
28851
28852         var styleDirective = valueFn({
28853           restrict: 'E',
28854           terminal: false
28855         });
28856
28857         var requiredDirective = function() {
28858           return {
28859             restrict: 'A',
28860             require: '?ngModel',
28861             link: function(scope, elm, attr, ctrl) {
28862               if (!ctrl) return;
28863               attr.required = true; // force truthy in case we are on non input element
28864
28865               ctrl.$validators.required = function(modelValue, viewValue) {
28866                 return !attr.required || !ctrl.$isEmpty(viewValue);
28867               };
28868
28869               attr.$observe('required', function() {
28870                 ctrl.$validate();
28871               });
28872             }
28873           };
28874         };
28875
28876
28877         var patternDirective = function() {
28878           return {
28879             restrict: 'A',
28880             require: '?ngModel',
28881             link: function(scope, elm, attr, ctrl) {
28882               if (!ctrl) return;
28883
28884               var regexp, patternExp = attr.ngPattern || attr.pattern;
28885               attr.$observe('pattern', function(regex) {
28886                 if (isString(regex) && regex.length > 0) {
28887                   regex = new RegExp('^' + regex + '$');
28888                 }
28889
28890                 if (regex && !regex.test) {
28891                   throw minErr('ngPattern')('noregexp',
28892                     'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28893                     regex, startingTag(elm));
28894                 }
28895
28896                 regexp = regex || undefined;
28897                 ctrl.$validate();
28898               });
28899
28900               ctrl.$validators.pattern = function(modelValue, viewValue) {
28901                 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28902                 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28903               };
28904             }
28905           };
28906         };
28907
28908
28909         var maxlengthDirective = function() {
28910           return {
28911             restrict: 'A',
28912             require: '?ngModel',
28913             link: function(scope, elm, attr, ctrl) {
28914               if (!ctrl) return;
28915
28916               var maxlength = -1;
28917               attr.$observe('maxlength', function(value) {
28918                 var intVal = toInt(value);
28919                 maxlength = isNaN(intVal) ? -1 : intVal;
28920                 ctrl.$validate();
28921               });
28922               ctrl.$validators.maxlength = function(modelValue, viewValue) {
28923                 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28924               };
28925             }
28926           };
28927         };
28928
28929         var minlengthDirective = function() {
28930           return {
28931             restrict: 'A',
28932             require: '?ngModel',
28933             link: function(scope, elm, attr, ctrl) {
28934               if (!ctrl) return;
28935
28936               var minlength = 0;
28937               attr.$observe('minlength', function(value) {
28938                 minlength = toInt(value) || 0;
28939                 ctrl.$validate();
28940               });
28941               ctrl.$validators.minlength = function(modelValue, viewValue) {
28942                 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28943               };
28944             }
28945           };
28946         };
28947
28948         if (window.angular.bootstrap) {
28949           //AngularJS is already loaded, so we can return here...
28950           console.log('WARNING: Tried to load angular more than once.');
28951           return;
28952         }
28953
28954         //try to bind to jquery now so that one can write jqLite(document).ready()
28955         //but we will rebind on bootstrap again.
28956         bindJQuery();
28957
28958         publishExternalAPI(angular);
28959
28960         angular.module("ngLocale", [], ["$provide", function($provide) {
28961         var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28962         function getDecimals(n) {
28963           n = n + '';
28964           var i = n.indexOf('.');
28965           return (i == -1) ? 0 : n.length - i - 1;
28966         }
28967
28968         function getVF(n, opt_precision) {
28969           var v = opt_precision;
28970
28971           if (undefined === v) {
28972             v = Math.min(getDecimals(n), 3);
28973           }
28974
28975           var base = Math.pow(10, v);
28976           var f = ((n * base) | 0) % base;
28977           return {v: v, f: f};
28978         }
28979
28980         $provide.value("$locale", {
28981           "DATETIME_FORMATS": {
28982             "AMPMS": [
28983               "AM",
28984               "PM"
28985             ],
28986             "DAY": [
28987               "Sunday",
28988               "Monday",
28989               "Tuesday",
28990               "Wednesday",
28991               "Thursday",
28992               "Friday",
28993               "Saturday"
28994             ],
28995             "ERANAMES": [
28996               "Before Christ",
28997               "Anno Domini"
28998             ],
28999             "ERAS": [
29000               "BC",
29001               "AD"
29002             ],
29003             "FIRSTDAYOFWEEK": 6,
29004             "MONTH": [
29005               "January",
29006               "February",
29007               "March",
29008               "April",
29009               "May",
29010               "June",
29011               "July",
29012               "August",
29013               "September",
29014               "October",
29015               "November",
29016               "December"
29017             ],
29018             "SHORTDAY": [
29019               "Sun",
29020               "Mon",
29021               "Tue",
29022               "Wed",
29023               "Thu",
29024               "Fri",
29025               "Sat"
29026             ],
29027             "SHORTMONTH": [
29028               "Jan",
29029               "Feb",
29030               "Mar",
29031               "Apr",
29032               "May",
29033               "Jun",
29034               "Jul",
29035               "Aug",
29036               "Sep",
29037               "Oct",
29038               "Nov",
29039               "Dec"
29040             ],
29041             "WEEKENDRANGE": [
29042               5,
29043               6
29044             ],
29045             "fullDate": "EEEE, MMMM d, y",
29046             "longDate": "MMMM d, y",
29047             "medium": "MMM d, y h:mm:ss a",
29048             "mediumDate": "MMM d, y",
29049             "mediumTime": "h:mm:ss a",
29050             "short": "M/d/yy h:mm a",
29051             "shortDate": "M/d/yy",
29052             "shortTime": "h:mm a"
29053           },
29054           "NUMBER_FORMATS": {
29055             "CURRENCY_SYM": "$",
29056             "DECIMAL_SEP": ".",
29057             "GROUP_SEP": ",",
29058             "PATTERNS": [
29059               {
29060                 "gSize": 3,
29061                 "lgSize": 3,
29062                 "maxFrac": 3,
29063                 "minFrac": 0,
29064                 "minInt": 1,
29065                 "negPre": "-",
29066                 "negSuf": "",
29067                 "posPre": "",
29068                 "posSuf": ""
29069               },
29070               {
29071                 "gSize": 3,
29072                 "lgSize": 3,
29073                 "maxFrac": 2,
29074                 "minFrac": 2,
29075                 "minInt": 1,
29076                 "negPre": "-\u00a4",
29077                 "negSuf": "",
29078                 "posPre": "\u00a4",
29079                 "posSuf": ""
29080               }
29081             ]
29082           },
29083           "id": "en-us",
29084           "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;}
29085         });
29086         }]);
29087
29088           jqLite(document).ready(function() {
29089             angularInit(document, bootstrap);
29090           });
29091
29092         })(window, document);
29093
29094         !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>');
29095
29096 /***/ },
29097 /* 3 */
29098 /***/ function(module, exports) {
29099
29100         /**
29101          * State-based routing for AngularJS
29102          * @version v0.2.15
29103          * @link http://angular-ui.github.com/
29104          * @license MIT License, http://www.opensource.org/licenses/MIT
29105          */
29106
29107         /* commonjs package manager support (eg componentjs) */
29108         if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
29109           module.exports = 'ui.router';
29110         }
29111
29112         (function (window, angular, undefined) {
29113         /*jshint globalstrict:true*/
29114         /*global angular:false*/
29115         'use strict';
29116
29117         var isDefined = angular.isDefined,
29118             isFunction = angular.isFunction,
29119             isString = angular.isString,
29120             isObject = angular.isObject,
29121             isArray = angular.isArray,
29122             forEach = angular.forEach,
29123             extend = angular.extend,
29124             copy = angular.copy;
29125
29126         function inherit(parent, extra) {
29127           return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29128         }
29129
29130         function merge(dst) {
29131           forEach(arguments, function(obj) {
29132             if (obj !== dst) {
29133               forEach(obj, function(value, key) {
29134                 if (!dst.hasOwnProperty(key)) dst[key] = value;
29135               });
29136             }
29137           });
29138           return dst;
29139         }
29140
29141         /**
29142          * Finds the common ancestor path between two states.
29143          *
29144          * @param {Object} first The first state.
29145          * @param {Object} second The second state.
29146          * @return {Array} Returns an array of state names in descending order, not including the root.
29147          */
29148         function ancestors(first, second) {
29149           var path = [];
29150
29151           for (var n in first.path) {
29152             if (first.path[n] !== second.path[n]) break;
29153             path.push(first.path[n]);
29154           }
29155           return path;
29156         }
29157
29158         /**
29159          * IE8-safe wrapper for `Object.keys()`.
29160          *
29161          * @param {Object} object A JavaScript object.
29162          * @return {Array} Returns the keys of the object as an array.
29163          */
29164         function objectKeys(object) {
29165           if (Object.keys) {
29166             return Object.keys(object);
29167           }
29168           var result = [];
29169
29170           forEach(object, function(val, key) {
29171             result.push(key);
29172           });
29173           return result;
29174         }
29175
29176         /**
29177          * IE8-safe wrapper for `Array.prototype.indexOf()`.
29178          *
29179          * @param {Array} array A JavaScript array.
29180          * @param {*} value A value to search the array for.
29181          * @return {Number} Returns the array index value of `value`, or `-1` if not present.
29182          */
29183         function indexOf(array, value) {
29184           if (Array.prototype.indexOf) {
29185             return array.indexOf(value, Number(arguments[2]) || 0);
29186           }
29187           var len = array.length >>> 0, from = Number(arguments[2]) || 0;
29188           from = (from < 0) ? Math.ceil(from) : Math.floor(from);
29189
29190           if (from < 0) from += len;
29191
29192           for (; from < len; from++) {
29193             if (from in array && array[from] === value) return from;
29194           }
29195           return -1;
29196         }
29197
29198         /**
29199          * Merges a set of parameters with all parameters inherited between the common parents of the
29200          * current state and a given destination state.
29201          *
29202          * @param {Object} currentParams The value of the current state parameters ($stateParams).
29203          * @param {Object} newParams The set of parameters which will be composited with inherited params.
29204          * @param {Object} $current Internal definition of object representing the current state.
29205          * @param {Object} $to Internal definition of object representing state to transition to.
29206          */
29207         function inheritParams(currentParams, newParams, $current, $to) {
29208           var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
29209
29210           for (var i in parents) {
29211             if (!parents[i].params) continue;
29212             parentParams = objectKeys(parents[i].params);
29213             if (!parentParams.length) continue;
29214
29215             for (var j in parentParams) {
29216               if (indexOf(inheritList, parentParams[j]) >= 0) continue;
29217               inheritList.push(parentParams[j]);
29218               inherited[parentParams[j]] = currentParams[parentParams[j]];
29219             }
29220           }
29221           return extend({}, inherited, newParams);
29222         }
29223
29224         /**
29225          * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
29226          *
29227          * @param {Object} a The first object.
29228          * @param {Object} b The second object.
29229          * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
29230          *                     it defaults to the list of keys in `a`.
29231          * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
29232          */
29233         function equalForKeys(a, b, keys) {
29234           if (!keys) {
29235             keys = [];
29236             for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
29237           }
29238
29239           for (var i=0; i<keys.length; i++) {
29240             var k = keys[i];
29241             if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
29242           }
29243           return true;
29244         }
29245
29246         /**
29247          * Returns the subset of an object, based on a list of keys.
29248          *
29249          * @param {Array} keys
29250          * @param {Object} values
29251          * @return {Boolean} Returns a subset of `values`.
29252          */
29253         function filterByKeys(keys, values) {
29254           var filtered = {};
29255
29256           forEach(keys, function (name) {
29257             filtered[name] = values[name];
29258           });
29259           return filtered;
29260         }
29261
29262         // like _.indexBy
29263         // when you know that your index values will be unique, or you want last-one-in to win
29264         function indexBy(array, propName) {
29265           var result = {};
29266           forEach(array, function(item) {
29267             result[item[propName]] = item;
29268           });
29269           return result;
29270         }
29271
29272         // extracted from underscore.js
29273         // Return a copy of the object only containing the whitelisted properties.
29274         function pick(obj) {
29275           var copy = {};
29276           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29277           forEach(keys, function(key) {
29278             if (key in obj) copy[key] = obj[key];
29279           });
29280           return copy;
29281         }
29282
29283         // extracted from underscore.js
29284         // Return a copy of the object omitting the blacklisted properties.
29285         function omit(obj) {
29286           var copy = {};
29287           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29288           for (var key in obj) {
29289             if (indexOf(keys, key) == -1) copy[key] = obj[key];
29290           }
29291           return copy;
29292         }
29293
29294         function pluck(collection, key) {
29295           var result = isArray(collection) ? [] : {};
29296
29297           forEach(collection, function(val, i) {
29298             result[i] = isFunction(key) ? key(val) : val[key];
29299           });
29300           return result;
29301         }
29302
29303         function filter(collection, callback) {
29304           var array = isArray(collection);
29305           var result = array ? [] : {};
29306           forEach(collection, function(val, i) {
29307             if (callback(val, i)) {
29308               result[array ? result.length : i] = val;
29309             }
29310           });
29311           return result;
29312         }
29313
29314         function map(collection, callback) {
29315           var result = isArray(collection) ? [] : {};
29316
29317           forEach(collection, function(val, i) {
29318             result[i] = callback(val, i);
29319           });
29320           return result;
29321         }
29322
29323         /**
29324          * @ngdoc overview
29325          * @name ui.router.util
29326          *
29327          * @description
29328          * # ui.router.util sub-module
29329          *
29330          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29331          * in your angular app (use {@link ui.router} module instead).
29332          *
29333          */
29334         angular.module('ui.router.util', ['ng']);
29335
29336         /**
29337          * @ngdoc overview
29338          * @name ui.router.router
29339          * 
29340          * @requires ui.router.util
29341          *
29342          * @description
29343          * # ui.router.router sub-module
29344          *
29345          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29346          * in your angular app (use {@link ui.router} module instead).
29347          */
29348         angular.module('ui.router.router', ['ui.router.util']);
29349
29350         /**
29351          * @ngdoc overview
29352          * @name ui.router.state
29353          * 
29354          * @requires ui.router.router
29355          * @requires ui.router.util
29356          *
29357          * @description
29358          * # ui.router.state sub-module
29359          *
29360          * This module is a dependency of the main ui.router module. Do not include this module as a dependency
29361          * in your angular app (use {@link ui.router} module instead).
29362          * 
29363          */
29364         angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
29365
29366         /**
29367          * @ngdoc overview
29368          * @name ui.router
29369          *
29370          * @requires ui.router.state
29371          *
29372          * @description
29373          * # ui.router
29374          * 
29375          * ## The main module for ui.router 
29376          * There are several sub-modules included with the ui.router module, however only this module is needed
29377          * as a dependency within your angular app. The other modules are for organization purposes. 
29378          *
29379          * The modules are:
29380          * * ui.router - the main "umbrella" module
29381          * * ui.router.router - 
29382          * 
29383          * *You'll need to include **only** this module as the dependency within your angular app.*
29384          * 
29385          * <pre>
29386          * <!doctype html>
29387          * <html ng-app="myApp">
29388          * <head>
29389          *   <script src="js/angular.js"></script>
29390          *   <!-- Include the ui-router script -->
29391          *   <script src="js/angular-ui-router.min.js"></script>
29392          *   <script>
29393          *     // ...and add 'ui.router' as a dependency
29394          *     var myApp = angular.module('myApp', ['ui.router']);
29395          *   </script>
29396          * </head>
29397          * <body>
29398          * </body>
29399          * </html>
29400          * </pre>
29401          */
29402         angular.module('ui.router', ['ui.router.state']);
29403
29404         angular.module('ui.router.compat', ['ui.router']);
29405
29406         /**
29407          * @ngdoc object
29408          * @name ui.router.util.$resolve
29409          *
29410          * @requires $q
29411          * @requires $injector
29412          *
29413          * @description
29414          * Manages resolution of (acyclic) graphs of promises.
29415          */
29416         $Resolve.$inject = ['$q', '$injector'];
29417         function $Resolve(  $q,    $injector) {
29418           
29419           var VISIT_IN_PROGRESS = 1,
29420               VISIT_DONE = 2,
29421               NOTHING = {},
29422               NO_DEPENDENCIES = [],
29423               NO_LOCALS = NOTHING,
29424               NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
29425           
29426
29427           /**
29428            * @ngdoc function
29429            * @name ui.router.util.$resolve#study
29430            * @methodOf ui.router.util.$resolve
29431            *
29432            * @description
29433            * Studies a set of invocables that are likely to be used multiple times.
29434            * <pre>
29435            * $resolve.study(invocables)(locals, parent, self)
29436            * </pre>
29437            * is equivalent to
29438            * <pre>
29439            * $resolve.resolve(invocables, locals, parent, self)
29440            * </pre>
29441            * but the former is more efficient (in fact `resolve` just calls `study` 
29442            * internally).
29443            *
29444            * @param {object} invocables Invocable objects
29445            * @return {function} a function to pass in locals, parent and self
29446            */
29447           this.study = function (invocables) {
29448             if (!isObject(invocables)) throw new Error("'invocables' must be an object");
29449             var invocableKeys = objectKeys(invocables || {});
29450             
29451             // Perform a topological sort of invocables to build an ordered plan
29452             var plan = [], cycle = [], visited = {};
29453             function visit(value, key) {
29454               if (visited[key] === VISIT_DONE) return;
29455               
29456               cycle.push(key);
29457               if (visited[key] === VISIT_IN_PROGRESS) {
29458                 cycle.splice(0, indexOf(cycle, key));
29459                 throw new Error("Cyclic dependency: " + cycle.join(" -> "));
29460               }
29461               visited[key] = VISIT_IN_PROGRESS;
29462               
29463               if (isString(value)) {
29464                 plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
29465               } else {
29466                 var params = $injector.annotate(value);
29467                 forEach(params, function (param) {
29468                   if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
29469                 });
29470                 plan.push(key, value, params);
29471               }
29472               
29473               cycle.pop();
29474               visited[key] = VISIT_DONE;
29475             }
29476             forEach(invocables, visit);
29477             invocables = cycle = visited = null; // plan is all that's required
29478             
29479             function isResolve(value) {
29480               return isObject(value) && value.then && value.$$promises;
29481             }
29482             
29483             return function (locals, parent, self) {
29484               if (isResolve(locals) && self === undefined) {
29485                 self = parent; parent = locals; locals = null;
29486               }
29487               if (!locals) locals = NO_LOCALS;
29488               else if (!isObject(locals)) {
29489                 throw new Error("'locals' must be an object");
29490               }       
29491               if (!parent) parent = NO_PARENT;
29492               else if (!isResolve(parent)) {
29493                 throw new Error("'parent' must be a promise returned by $resolve.resolve()");
29494               }
29495               
29496               // To complete the overall resolution, we have to wait for the parent
29497               // promise and for the promise for each invokable in our plan.
29498               var resolution = $q.defer(),
29499                   result = resolution.promise,
29500                   promises = result.$$promises = {},
29501                   values = extend({}, locals),
29502                   wait = 1 + plan.length/3,
29503                   merged = false;
29504                   
29505               function done() {
29506                 // Merge parent values we haven't got yet and publish our own $$values
29507                 if (!--wait) {
29508                   if (!merged) merge(values, parent.$$values); 
29509                   result.$$values = values;
29510                   result.$$promises = result.$$promises || true; // keep for isResolve()
29511                   delete result.$$inheritedValues;
29512                   resolution.resolve(values);
29513                 }
29514               }
29515               
29516               function fail(reason) {
29517                 result.$$failure = reason;
29518                 resolution.reject(reason);
29519               }
29520
29521               // Short-circuit if parent has already failed
29522               if (isDefined(parent.$$failure)) {
29523                 fail(parent.$$failure);
29524                 return result;
29525               }
29526               
29527               if (parent.$$inheritedValues) {
29528                 merge(values, omit(parent.$$inheritedValues, invocableKeys));
29529               }
29530
29531               // Merge parent values if the parent has already resolved, or merge
29532               // parent promises and wait if the parent resolve is still in progress.
29533               extend(promises, parent.$$promises);
29534               if (parent.$$values) {
29535                 merged = merge(values, omit(parent.$$values, invocableKeys));
29536                 result.$$inheritedValues = omit(parent.$$values, invocableKeys);
29537                 done();
29538               } else {
29539                 if (parent.$$inheritedValues) {
29540                   result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
29541                 }        
29542                 parent.then(done, fail);
29543               }
29544               
29545               // Process each invocable in the plan, but ignore any where a local of the same name exists.
29546               for (var i=0, ii=plan.length; i<ii; i+=3) {
29547                 if (locals.hasOwnProperty(plan[i])) done();
29548                 else invoke(plan[i], plan[i+1], plan[i+2]);
29549               }
29550               
29551               function invoke(key, invocable, params) {
29552                 // Create a deferred for this invocation. Failures will propagate to the resolution as well.
29553                 var invocation = $q.defer(), waitParams = 0;
29554                 function onfailure(reason) {
29555                   invocation.reject(reason);
29556                   fail(reason);
29557                 }
29558                 // Wait for any parameter that we have a promise for (either from parent or from this
29559                 // resolve; in that case study() will have made sure it's ordered before us in the plan).
29560                 forEach(params, function (dep) {
29561                   if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
29562                     waitParams++;
29563                     promises[dep].then(function (result) {
29564                       values[dep] = result;
29565                       if (!(--waitParams)) proceed();
29566                     }, onfailure);
29567                   }
29568                 });
29569                 if (!waitParams) proceed();
29570                 function proceed() {
29571                   if (isDefined(result.$$failure)) return;
29572                   try {
29573                     invocation.resolve($injector.invoke(invocable, self, values));
29574                     invocation.promise.then(function (result) {
29575                       values[key] = result;
29576                       done();
29577                     }, onfailure);
29578                   } catch (e) {
29579                     onfailure(e);
29580                   }
29581                 }
29582                 // Publish promise synchronously; invocations further down in the plan may depend on it.
29583                 promises[key] = invocation.promise;
29584               }
29585               
29586               return result;
29587             };
29588           };
29589           
29590           /**
29591            * @ngdoc function
29592            * @name ui.router.util.$resolve#resolve
29593            * @methodOf ui.router.util.$resolve
29594            *
29595            * @description
29596            * Resolves a set of invocables. An invocable is a function to be invoked via 
29597            * `$injector.invoke()`, and can have an arbitrary number of dependencies. 
29598            * An invocable can either return a value directly,
29599            * or a `$q` promise. If a promise is returned it will be resolved and the 
29600            * resulting value will be used instead. Dependencies of invocables are resolved 
29601            * (in this order of precedence)
29602            *
29603            * - from the specified `locals`
29604            * - from another invocable that is part of this `$resolve` call
29605            * - from an invocable that is inherited from a `parent` call to `$resolve` 
29606            *   (or recursively
29607            * - from any ancestor `$resolve` of that parent).
29608            *
29609            * The return value of `$resolve` is a promise for an object that contains 
29610            * (in this order of precedence)
29611            *
29612            * - any `locals` (if specified)
29613            * - the resolved return values of all injectables
29614            * - any values inherited from a `parent` call to `$resolve` (if specified)
29615            *
29616            * The promise will resolve after the `parent` promise (if any) and all promises 
29617            * returned by injectables have been resolved. If any invocable 
29618            * (or `$injector.invoke`) throws an exception, or if a promise returned by an 
29619            * invocable is rejected, the `$resolve` promise is immediately rejected with the 
29620            * same error. A rejection of a `parent` promise (if specified) will likewise be 
29621            * propagated immediately. Once the `$resolve` promise has been rejected, no 
29622            * further invocables will be called.
29623            * 
29624            * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
29625            * to throw an error. As a special case, an injectable can depend on a parameter 
29626            * with the same name as the injectable, which will be fulfilled from the `parent` 
29627            * injectable of the same name. This allows inherited values to be decorated. 
29628            * Note that in this case any other injectable in the same `$resolve` with the same
29629            * dependency would see the decorated value, not the inherited value.
29630            *
29631            * Note that missing dependencies -- unlike cyclic dependencies -- will cause an 
29632            * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous) 
29633            * exception.
29634            *
29635            * Invocables are invoked eagerly as soon as all dependencies are available. 
29636            * This is true even for dependencies inherited from a `parent` call to `$resolve`.
29637            *
29638            * As a special case, an invocable can be a string, in which case it is taken to 
29639            * be a service name to be passed to `$injector.get()`. This is supported primarily 
29640            * for backwards-compatibility with the `resolve` property of `$routeProvider` 
29641            * routes.
29642            *
29643            * @param {object} invocables functions to invoke or 
29644            * `$injector` services to fetch.
29645            * @param {object} locals  values to make available to the injectables
29646            * @param {object} parent  a promise returned by another call to `$resolve`.
29647            * @param {object} self  the `this` for the invoked methods
29648            * @return {object} Promise for an object that contains the resolved return value
29649            * of all invocables, as well as any inherited and local values.
29650            */
29651           this.resolve = function (invocables, locals, parent, self) {
29652             return this.study(invocables)(locals, parent, self);
29653           };
29654         }
29655
29656         angular.module('ui.router.util').service('$resolve', $Resolve);
29657
29658
29659         /**
29660          * @ngdoc object
29661          * @name ui.router.util.$templateFactory
29662          *
29663          * @requires $http
29664          * @requires $templateCache
29665          * @requires $injector
29666          *
29667          * @description
29668          * Service. Manages loading of templates.
29669          */
29670         $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
29671         function $TemplateFactory(  $http,   $templateCache,   $injector) {
29672
29673           /**
29674            * @ngdoc function
29675            * @name ui.router.util.$templateFactory#fromConfig
29676            * @methodOf ui.router.util.$templateFactory
29677            *
29678            * @description
29679            * Creates a template from a configuration object. 
29680            *
29681            * @param {object} config Configuration object for which to load a template. 
29682            * The following properties are search in the specified order, and the first one 
29683            * that is defined is used to create the template:
29684            *
29685            * @param {string|object} config.template html string template or function to 
29686            * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29687            * @param {string|object} config.templateUrl url to load or a function returning 
29688            * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
29689            * @param {Function} config.templateProvider function to invoke via 
29690            * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
29691            * @param {object} params  Parameters to pass to the template function.
29692            * @param {object} locals Locals to pass to `invoke` if the template is loaded 
29693            * via a `templateProvider`. Defaults to `{ params: params }`.
29694            *
29695            * @return {string|object}  The template html as a string, or a promise for 
29696            * that string,or `null` if no template is configured.
29697            */
29698           this.fromConfig = function (config, params, locals) {
29699             return (
29700               isDefined(config.template) ? this.fromString(config.template, params) :
29701               isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
29702               isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
29703               null
29704             );
29705           };
29706
29707           /**
29708            * @ngdoc function
29709            * @name ui.router.util.$templateFactory#fromString
29710            * @methodOf ui.router.util.$templateFactory
29711            *
29712            * @description
29713            * Creates a template from a string or a function returning a string.
29714            *
29715            * @param {string|object} template html template as a string or function that 
29716            * returns an html template as a string.
29717            * @param {object} params Parameters to pass to the template function.
29718            *
29719            * @return {string|object} The template html as a string, or a promise for that 
29720            * string.
29721            */
29722           this.fromString = function (template, params) {
29723             return isFunction(template) ? template(params) : template;
29724           };
29725
29726           /**
29727            * @ngdoc function
29728            * @name ui.router.util.$templateFactory#fromUrl
29729            * @methodOf ui.router.util.$templateFactory
29730            * 
29731            * @description
29732            * Loads a template from the a URL via `$http` and `$templateCache`.
29733            *
29734            * @param {string|Function} url url of the template to load, or a function 
29735            * that returns a url.
29736            * @param {Object} params Parameters to pass to the url function.
29737            * @return {string|Promise.<string>} The template html as a string, or a promise 
29738            * for that string.
29739            */
29740           this.fromUrl = function (url, params) {
29741             if (isFunction(url)) url = url(params);
29742             if (url == null) return null;
29743             else return $http
29744                 .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
29745                 .then(function(response) { return response.data; });
29746           };
29747
29748           /**
29749            * @ngdoc function
29750            * @name ui.router.util.$templateFactory#fromProvider
29751            * @methodOf ui.router.util.$templateFactory
29752            *
29753            * @description
29754            * Creates a template by invoking an injectable provider function.
29755            *
29756            * @param {Function} provider Function to invoke via `$injector.invoke`
29757            * @param {Object} params Parameters for the template.
29758            * @param {Object} locals Locals to pass to `invoke`. Defaults to 
29759            * `{ params: params }`.
29760            * @return {string|Promise.<string>} The template html as a string, or a promise 
29761            * for that string.
29762            */
29763           this.fromProvider = function (provider, params, locals) {
29764             return $injector.invoke(provider, null, locals || { params: params });
29765           };
29766         }
29767
29768         angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
29769
29770         var $$UMFP; // reference to $UrlMatcherFactoryProvider
29771
29772         /**
29773          * @ngdoc object
29774          * @name ui.router.util.type:UrlMatcher
29775          *
29776          * @description
29777          * Matches URLs against patterns and extracts named parameters from the path or the search
29778          * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
29779          * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
29780          * do not influence whether or not a URL is matched, but their values are passed through into
29781          * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
29782          *
29783          * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
29784          * syntax, which optionally allows a regular expression for the parameter to be specified:
29785          *
29786          * * `':'` name - colon placeholder
29787          * * `'*'` name - catch-all placeholder
29788          * * `'{' name '}'` - curly placeholder
29789          * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
29790          *   regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
29791          *
29792          * Parameter names may contain only word characters (latin letters, digits, and underscore) and
29793          * must be unique within the pattern (across both path and search parameters). For colon
29794          * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
29795          * number of characters other than '/'. For catch-all placeholders the path parameter matches
29796          * any number of characters.
29797          *
29798          * Examples:
29799          *
29800          * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
29801          *   trailing slashes, and patterns have to match the entire path, not just a prefix.
29802          * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
29803          *   '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
29804          * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
29805          * * `'/user/{id:[^/]*}'` - Same as the previous example.
29806          * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
29807          *   parameter consists of 1 to 8 hex digits.
29808          * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
29809          *   path into the parameter 'path'.
29810          * * `'/files/*path'` - ditto.
29811          * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
29812          *   in the built-in  `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
29813          *
29814          * @param {string} pattern  The pattern to compile into a matcher.
29815          * @param {Object} config  A configuration object hash:
29816          * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
29817          *   an existing UrlMatcher
29818          *
29819          * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
29820          * * `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`.
29821          *
29822          * @property {string} prefix  A static prefix of this pattern. The matcher guarantees that any
29823          *   URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
29824          *   non-null) will start with this prefix.
29825          *
29826          * @property {string} source  The pattern that was passed into the constructor
29827          *
29828          * @property {string} sourcePath  The path portion of the source property
29829          *
29830          * @property {string} sourceSearch  The search portion of the source property
29831          *
29832          * @property {string} regex  The constructed regex that will be used to match against the url when
29833          *   it is time to determine which url will match.
29834          *
29835          * @returns {Object}  New `UrlMatcher` object
29836          */
29837         function UrlMatcher(pattern, config, parentMatcher) {
29838           config = extend({ params: {} }, isObject(config) ? config : {});
29839
29840           // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
29841           //   '*' name
29842           //   ':' name
29843           //   '{' name '}'
29844           //   '{' name ':' regexp '}'
29845           // The regular expression is somewhat complicated due to the need to allow curly braces
29846           // inside the regular expression. The placeholder regexp breaks down as follows:
29847           //    ([:*])([\w\[\]]+)              - classic placeholder ($1 / $2) (search version has - for snake-case)
29848           //    \{([\w\[\]]+)(?:\:( ... ))?\}  - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29849           //    (?: ... | ... | ... )+         - the regexp consists of any number of atoms, an atom being either
29850           //    [^{}\\]+                       - anything other than curly braces or backslash
29851           //    \\.                            - a backslash escape
29852           //    \{(?:[^{}\\]+|\\.)*\}          - a matched set of curly braces containing other atoms
29853           var placeholder       = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29854               searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29855               compiled = '^', last = 0, m,
29856               segments = this.segments = [],
29857               parentParams = parentMatcher ? parentMatcher.params : {},
29858               params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
29859               paramNames = [];
29860
29861           function addParameter(id, type, config, location) {
29862             paramNames.push(id);
29863             if (parentParams[id]) return parentParams[id];
29864             if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29865             if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
29866             params[id] = new $$UMFP.Param(id, type, config, location);
29867             return params[id];
29868           }
29869
29870           function quoteRegExp(string, pattern, squash, optional) {
29871             var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
29872             if (!pattern) return result;
29873             switch(squash) {
29874               case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
29875               case true:  surroundPattern = ['?(', ')?']; break;
29876               default:    surroundPattern = ['(' + squash + "|", ')?']; break;
29877             }
29878             return result + surroundPattern[0] + pattern + surroundPattern[1];
29879           }
29880
29881           this.source = pattern;
29882
29883           // Split into static segments separated by path parameter placeholders.
29884           // The number of segments is always 1 more than the number of parameters.
29885           function matchDetails(m, isSearch) {
29886             var id, regexp, segment, type, cfg, arrayMode;
29887             id          = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
29888             cfg         = config.params[id];
29889             segment     = pattern.substring(last, m.index);
29890             regexp      = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
29891             type        = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29892             return {
29893               id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
29894             };
29895           }
29896
29897           var p, param, segment;
29898           while ((m = placeholder.exec(pattern))) {
29899             p = matchDetails(m, false);
29900             if (p.segment.indexOf('?') >= 0) break; // we're into the search part
29901
29902             param = addParameter(p.id, p.type, p.cfg, "path");
29903             compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
29904             segments.push(p.segment);
29905             last = placeholder.lastIndex;
29906           }
29907           segment = pattern.substring(last);
29908
29909           // Find any search parameter names and remove them from the last segment
29910           var i = segment.indexOf('?');
29911
29912           if (i >= 0) {
29913             var search = this.sourceSearch = segment.substring(i);
29914             segment = segment.substring(0, i);
29915             this.sourcePath = pattern.substring(0, last + i);
29916
29917             if (search.length > 0) {
29918               last = 0;
29919               while ((m = searchPlaceholder.exec(search))) {
29920                 p = matchDetails(m, true);
29921                 param = addParameter(p.id, p.type, p.cfg, "search");
29922                 last = placeholder.lastIndex;
29923                 // check if ?&
29924               }
29925             }
29926           } else {
29927             this.sourcePath = pattern;
29928             this.sourceSearch = '';
29929           }
29930
29931           compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
29932           segments.push(segment);
29933
29934           this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
29935           this.prefix = segments[0];
29936           this.$$paramNames = paramNames;
29937         }
29938
29939         /**
29940          * @ngdoc function
29941          * @name ui.router.util.type:UrlMatcher#concat
29942          * @methodOf ui.router.util.type:UrlMatcher
29943          *
29944          * @description
29945          * Returns a new matcher for a pattern constructed by appending the path part and adding the
29946          * search parameters of the specified pattern to this pattern. The current pattern is not
29947          * modified. This can be understood as creating a pattern for URLs that are relative to (or
29948          * suffixes of) the current pattern.
29949          *
29950          * @example
29951          * The following two matchers are equivalent:
29952          * <pre>
29953          * new UrlMatcher('/user/{id}?q').concat('/details?date');
29954          * new UrlMatcher('/user/{id}/details?q&date');
29955          * </pre>
29956          *
29957          * @param {string} pattern  The pattern to append.
29958          * @param {Object} config  An object hash of the configuration for the matcher.
29959          * @returns {UrlMatcher}  A matcher for the concatenated pattern.
29960          */
29961         UrlMatcher.prototype.concat = function (pattern, config) {
29962           // Because order of search parameters is irrelevant, we can add our own search
29963           // parameters to the end of the new pattern. Parse the new pattern by itself
29964           // and then join the bits together, but it's much easier to do this on a string level.
29965           var defaultConfig = {
29966             caseInsensitive: $$UMFP.caseInsensitive(),
29967             strict: $$UMFP.strictMode(),
29968             squash: $$UMFP.defaultSquashPolicy()
29969           };
29970           return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
29971         };
29972
29973         UrlMatcher.prototype.toString = function () {
29974           return this.source;
29975         };
29976
29977         /**
29978          * @ngdoc function
29979          * @name ui.router.util.type:UrlMatcher#exec
29980          * @methodOf ui.router.util.type:UrlMatcher
29981          *
29982          * @description
29983          * Tests the specified path against this matcher, and returns an object containing the captured
29984          * parameter values, or null if the path does not match. The returned object contains the values
29985          * of any search parameters that are mentioned in the pattern, but their value may be null if
29986          * they are not present in `searchParams`. This means that search parameters are always treated
29987          * as optional.
29988          *
29989          * @example
29990          * <pre>
29991          * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
29992          *   x: '1', q: 'hello'
29993          * });
29994          * // returns { id: 'bob', q: 'hello', r: null }
29995          * </pre>
29996          *
29997          * @param {string} path  The URL path to match, e.g. `$location.path()`.
29998          * @param {Object} searchParams  URL search parameters, e.g. `$location.search()`.
29999          * @returns {Object}  The captured parameter values.
30000          */
30001         UrlMatcher.prototype.exec = function (path, searchParams) {
30002           var m = this.regexp.exec(path);
30003           if (!m) return null;
30004           searchParams = searchParams || {};
30005
30006           var paramNames = this.parameters(), nTotal = paramNames.length,
30007             nPath = this.segments.length - 1,
30008             values = {}, i, j, cfg, paramName;
30009
30010           if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
30011
30012           function decodePathArray(string) {
30013             function reverseString(str) { return str.split("").reverse().join(""); }
30014             function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
30015
30016             var split = reverseString(string).split(/-(?!\\)/);
30017             var allReversed = map(split, reverseString);
30018             return map(allReversed, unquoteDashes).reverse();
30019           }
30020
30021           for (i = 0; i < nPath; i++) {
30022             paramName = paramNames[i];
30023             var param = this.params[paramName];
30024             var paramVal = m[i+1];
30025             // if the param value matches a pre-replace pair, replace the value before decoding.
30026             for (j = 0; j < param.replace; j++) {
30027               if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30028             }
30029             if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30030             values[paramName] = param.value(paramVal);
30031           }
30032           for (/**/; i < nTotal; i++) {
30033             paramName = paramNames[i];
30034             values[paramName] = this.params[paramName].value(searchParams[paramName]);
30035           }
30036
30037           return values;
30038         };
30039
30040         /**
30041          * @ngdoc function
30042          * @name ui.router.util.type:UrlMatcher#parameters
30043          * @methodOf ui.router.util.type:UrlMatcher
30044          *
30045          * @description
30046          * Returns the names of all path and search parameters of this pattern in an unspecified order.
30047          *
30048          * @returns {Array.<string>}  An array of parameter names. Must be treated as read-only. If the
30049          *    pattern has no parameters, an empty array is returned.
30050          */
30051         UrlMatcher.prototype.parameters = function (param) {
30052           if (!isDefined(param)) return this.$$paramNames;
30053           return this.params[param] || null;
30054         };
30055
30056         /**
30057          * @ngdoc function
30058          * @name ui.router.util.type:UrlMatcher#validate
30059          * @methodOf ui.router.util.type:UrlMatcher
30060          *
30061          * @description
30062          * Checks an object hash of parameters to validate their correctness according to the parameter
30063          * types of this `UrlMatcher`.
30064          *
30065          * @param {Object} params The object hash of parameters to validate.
30066          * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
30067          */
30068         UrlMatcher.prototype.validates = function (params) {
30069           return this.params.$$validates(params);
30070         };
30071
30072         /**
30073          * @ngdoc function
30074          * @name ui.router.util.type:UrlMatcher#format
30075          * @methodOf ui.router.util.type:UrlMatcher
30076          *
30077          * @description
30078          * Creates a URL that matches this pattern by substituting the specified values
30079          * for the path and search parameters. Null values for path parameters are
30080          * treated as empty strings.
30081          *
30082          * @example
30083          * <pre>
30084          * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
30085          * // returns '/user/bob?q=yes'
30086          * </pre>
30087          *
30088          * @param {Object} values  the values to substitute for the parameters in this pattern.
30089          * @returns {string}  the formatted URL (path and optionally search part).
30090          */
30091         UrlMatcher.prototype.format = function (values) {
30092           values = values || {};
30093           var segments = this.segments, params = this.parameters(), paramset = this.params;
30094           if (!this.validates(values)) return null;
30095
30096           var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
30097
30098           function encodeDashes(str) { // Replace dashes with encoded "\-"
30099             return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
30100           }
30101
30102           for (i = 0; i < nTotal; i++) {
30103             var isPathParam = i < nPath;
30104             var name = params[i], param = paramset[name], value = param.value(values[name]);
30105             var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
30106             var squash = isDefaultValue ? param.squash : false;
30107             var encoded = param.type.encode(value);
30108
30109             if (isPathParam) {
30110               var nextSegment = segments[i + 1];
30111               if (squash === false) {
30112                 if (encoded != null) {
30113                   if (isArray(encoded)) {
30114                     result += map(encoded, encodeDashes).join("-");
30115                   } else {
30116                     result += encodeURIComponent(encoded);
30117                   }
30118                 }
30119                 result += nextSegment;
30120               } else if (squash === true) {
30121                 var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
30122                 result += nextSegment.match(capture)[1];
30123               } else if (isString(squash)) {
30124                 result += squash + nextSegment;
30125               }
30126             } else {
30127               if (encoded == null || (isDefaultValue && squash !== false)) continue;
30128               if (!isArray(encoded)) encoded = [ encoded ];
30129               encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
30130               result += (search ? '&' : '?') + (name + '=' + encoded);
30131               search = true;
30132             }
30133           }
30134
30135           return result;
30136         };
30137
30138         /**
30139          * @ngdoc object
30140          * @name ui.router.util.type:Type
30141          *
30142          * @description
30143          * Implements an interface to define custom parameter types that can be decoded from and encoded to
30144          * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
30145          * objects when matching or formatting URLs, or comparing or validating parameter values.
30146          *
30147          * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
30148          * information on registering custom types.
30149          *
30150          * @param {Object} config  A configuration object which contains the custom type definition.  The object's
30151          *        properties will override the default methods and/or pattern in `Type`'s public interface.
30152          * @example
30153          * <pre>
30154          * {
30155          *   decode: function(val) { return parseInt(val, 10); },
30156          *   encode: function(val) { return val && val.toString(); },
30157          *   equals: function(a, b) { return this.is(a) && a === b; },
30158          *   is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
30159          *   pattern: /\d+/
30160          * }
30161          * </pre>
30162          *
30163          * @property {RegExp} pattern The regular expression pattern used to match values of this type when
30164          *           coming from a substring of a URL.
30165          *
30166          * @returns {Object}  Returns a new `Type` object.
30167          */
30168         function Type(config) {
30169           extend(this, config);
30170         }
30171
30172         /**
30173          * @ngdoc function
30174          * @name ui.router.util.type:Type#is
30175          * @methodOf ui.router.util.type:Type
30176          *
30177          * @description
30178          * Detects whether a value is of a particular type. Accepts a native (decoded) value
30179          * and determines whether it matches the current `Type` object.
30180          *
30181          * @param {*} val  The value to check.
30182          * @param {string} key  Optional. If the type check is happening in the context of a specific
30183          *        {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
30184          *        parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
30185          * @returns {Boolean}  Returns `true` if the value matches the type, otherwise `false`.
30186          */
30187         Type.prototype.is = function(val, key) {
30188           return true;
30189         };
30190
30191         /**
30192          * @ngdoc function
30193          * @name ui.router.util.type:Type#encode
30194          * @methodOf ui.router.util.type:Type
30195          *
30196          * @description
30197          * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
30198          * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
30199          * only needs to be a representation of `val` that has been coerced to a string.
30200          *
30201          * @param {*} val  The value to encode.
30202          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30203          *        meta-programming of `Type` objects.
30204          * @returns {string}  Returns a string representation of `val` that can be encoded in a URL.
30205          */
30206         Type.prototype.encode = function(val, key) {
30207           return val;
30208         };
30209
30210         /**
30211          * @ngdoc function
30212          * @name ui.router.util.type:Type#decode
30213          * @methodOf ui.router.util.type:Type
30214          *
30215          * @description
30216          * Converts a parameter value (from URL string or transition param) to a custom/native value.
30217          *
30218          * @param {string} val  The URL parameter value to decode.
30219          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30220          *        meta-programming of `Type` objects.
30221          * @returns {*}  Returns a custom representation of the URL parameter value.
30222          */
30223         Type.prototype.decode = function(val, key) {
30224           return val;
30225         };
30226
30227         /**
30228          * @ngdoc function
30229          * @name ui.router.util.type:Type#equals
30230          * @methodOf ui.router.util.type:Type
30231          *
30232          * @description
30233          * Determines whether two decoded values are equivalent.
30234          *
30235          * @param {*} a  A value to compare against.
30236          * @param {*} b  A value to compare against.
30237          * @returns {Boolean}  Returns `true` if the values are equivalent/equal, otherwise `false`.
30238          */
30239         Type.prototype.equals = function(a, b) {
30240           return a == b;
30241         };
30242
30243         Type.prototype.$subPattern = function() {
30244           var sub = this.pattern.toString();
30245           return sub.substr(1, sub.length - 2);
30246         };
30247
30248         Type.prototype.pattern = /.*/;
30249
30250         Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
30251
30252         /** Given an encoded string, or a decoded object, returns a decoded object */
30253         Type.prototype.$normalize = function(val) {
30254           return this.is(val) ? val : this.decode(val);
30255         };
30256
30257         /*
30258          * Wraps an existing custom Type as an array of Type, depending on 'mode'.
30259          * e.g.:
30260          * - urlmatcher pattern "/path?{queryParam[]:int}"
30261          * - url: "/path?queryParam=1&queryParam=2
30262          * - $stateParams.queryParam will be [1, 2]
30263          * if `mode` is "auto", then
30264          * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
30265          * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
30266          */
30267         Type.prototype.$asArray = function(mode, isSearch) {
30268           if (!mode) return this;
30269           if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
30270
30271           function ArrayType(type, mode) {
30272             function bindTo(type, callbackName) {
30273               return function() {
30274                 return type[callbackName].apply(type, arguments);
30275               };
30276             }
30277
30278             // Wrap non-array value as array
30279             function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
30280             // Unwrap array value for "auto" mode. Return undefined for empty array.
30281             function arrayUnwrap(val) {
30282               switch(val.length) {
30283                 case 0: return undefined;
30284                 case 1: return mode === "auto" ? val[0] : val;
30285                 default: return val;
30286               }
30287             }
30288             function falsey(val) { return !val; }
30289
30290             // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
30291             function arrayHandler(callback, allTruthyMode) {
30292               return function handleArray(val) {
30293                 val = arrayWrap(val);
30294                 var result = map(val, callback);
30295                 if (allTruthyMode === true)
30296                   return filter(result, falsey).length === 0;
30297                 return arrayUnwrap(result);
30298               };
30299             }
30300
30301             // Wraps type (.equals) functions to operate on each value of an array
30302             function arrayEqualsHandler(callback) {
30303               return function handleArray(val1, val2) {
30304                 var left = arrayWrap(val1), right = arrayWrap(val2);
30305                 if (left.length !== right.length) return false;
30306                 for (var i = 0; i < left.length; i++) {
30307                   if (!callback(left[i], right[i])) return false;
30308                 }
30309                 return true;
30310               };
30311             }
30312
30313             this.encode = arrayHandler(bindTo(type, 'encode'));
30314             this.decode = arrayHandler(bindTo(type, 'decode'));
30315             this.is     = arrayHandler(bindTo(type, 'is'), true);
30316             this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
30317             this.pattern = type.pattern;
30318             this.$normalize = arrayHandler(bindTo(type, '$normalize'));
30319             this.name = type.name;
30320             this.$arrayMode = mode;
30321           }
30322
30323           return new ArrayType(this, mode);
30324         };
30325
30326
30327
30328         /**
30329          * @ngdoc object
30330          * @name ui.router.util.$urlMatcherFactory
30331          *
30332          * @description
30333          * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
30334          * is also available to providers under the name `$urlMatcherFactoryProvider`.
30335          */
30336         function $UrlMatcherFactory() {
30337           $$UMFP = this;
30338
30339           var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
30340
30341           function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
30342           function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
30343
30344           var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30345             string: {
30346               encode: valToString,
30347               decode: valFromString,
30348               // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
30349               // In 0.2.x, string params are optional by default for backwards compat
30350               is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
30351               pattern: /[^/]*/
30352             },
30353             int: {
30354               encode: valToString,
30355               decode: function(val) { return parseInt(val, 10); },
30356               is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
30357               pattern: /\d+/
30358             },
30359             bool: {
30360               encode: function(val) { return val ? 1 : 0; },
30361               decode: function(val) { return parseInt(val, 10) !== 0; },
30362               is: function(val) { return val === true || val === false; },
30363               pattern: /0|1/
30364             },
30365             date: {
30366               encode: function (val) {
30367                 if (!this.is(val))
30368                   return undefined;
30369                 return [ val.getFullYear(),
30370                   ('0' + (val.getMonth() + 1)).slice(-2),
30371                   ('0' + val.getDate()).slice(-2)
30372                 ].join("-");
30373               },
30374               decode: function (val) {
30375                 if (this.is(val)) return val;
30376                 var match = this.capture.exec(val);
30377                 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
30378               },
30379               is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
30380               equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
30381               pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
30382               capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
30383             },
30384             json: {
30385               encode: angular.toJson,
30386               decode: angular.fromJson,
30387               is: angular.isObject,
30388               equals: angular.equals,
30389               pattern: /[^/]*/
30390             },
30391             any: { // does not encode/decode
30392               encode: angular.identity,
30393               decode: angular.identity,
30394               equals: angular.equals,
30395               pattern: /.*/
30396             }
30397           };
30398
30399           function getDefaultConfig() {
30400             return {
30401               strict: isStrictMode,
30402               caseInsensitive: isCaseInsensitive
30403             };
30404           }
30405
30406           function isInjectable(value) {
30407             return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
30408           }
30409
30410           /**
30411            * [Internal] Get the default value of a parameter, which may be an injectable function.
30412            */
30413           $UrlMatcherFactory.$$getDefaultValue = function(config) {
30414             if (!isInjectable(config.value)) return config.value;
30415             if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30416             return injector.invoke(config.value);
30417           };
30418
30419           /**
30420            * @ngdoc function
30421            * @name ui.router.util.$urlMatcherFactory#caseInsensitive
30422            * @methodOf ui.router.util.$urlMatcherFactory
30423            *
30424            * @description
30425            * Defines whether URL matching should be case sensitive (the default behavior), or not.
30426            *
30427            * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
30428            * @returns {boolean} the current value of caseInsensitive
30429            */
30430           this.caseInsensitive = function(value) {
30431             if (isDefined(value))
30432               isCaseInsensitive = value;
30433             return isCaseInsensitive;
30434           };
30435
30436           /**
30437            * @ngdoc function
30438            * @name ui.router.util.$urlMatcherFactory#strictMode
30439            * @methodOf ui.router.util.$urlMatcherFactory
30440            *
30441            * @description
30442            * Defines whether URLs should match trailing slashes, or not (the default behavior).
30443            *
30444            * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
30445            * @returns {boolean} the current value of strictMode
30446            */
30447           this.strictMode = function(value) {
30448             if (isDefined(value))
30449               isStrictMode = value;
30450             return isStrictMode;
30451           };
30452
30453           /**
30454            * @ngdoc function
30455            * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
30456            * @methodOf ui.router.util.$urlMatcherFactory
30457            *
30458            * @description
30459            * Sets the default behavior when generating or matching URLs with default parameter values.
30460            *
30461            * @param {string} value A string that defines the default parameter URL squashing behavior.
30462            *    `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
30463            *    `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
30464            *             parameter is surrounded by slashes, squash (remove) one slash from the URL
30465            *    any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
30466            *             the parameter value from the URL and replace it with this string.
30467            */
30468           this.defaultSquashPolicy = function(value) {
30469             if (!isDefined(value)) return defaultSquashPolicy;
30470             if (value !== true && value !== false && !isString(value))
30471               throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
30472             defaultSquashPolicy = value;
30473             return value;
30474           };
30475
30476           /**
30477            * @ngdoc function
30478            * @name ui.router.util.$urlMatcherFactory#compile
30479            * @methodOf ui.router.util.$urlMatcherFactory
30480            *
30481            * @description
30482            * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
30483            *
30484            * @param {string} pattern  The URL pattern.
30485            * @param {Object} config  The config object hash.
30486            * @returns {UrlMatcher}  The UrlMatcher.
30487            */
30488           this.compile = function (pattern, config) {
30489             return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
30490           };
30491
30492           /**
30493            * @ngdoc function
30494            * @name ui.router.util.$urlMatcherFactory#isMatcher
30495            * @methodOf ui.router.util.$urlMatcherFactory
30496            *
30497            * @description
30498            * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
30499            *
30500            * @param {Object} object  The object to perform the type check against.
30501            * @returns {Boolean}  Returns `true` if the object matches the `UrlMatcher` interface, by
30502            *          implementing all the same methods.
30503            */
30504           this.isMatcher = function (o) {
30505             if (!isObject(o)) return false;
30506             var result = true;
30507
30508             forEach(UrlMatcher.prototype, function(val, name) {
30509               if (isFunction(val)) {
30510                 result = result && (isDefined(o[name]) && isFunction(o[name]));
30511               }
30512             });
30513             return result;
30514           };
30515
30516           /**
30517            * @ngdoc function
30518            * @name ui.router.util.$urlMatcherFactory#type
30519            * @methodOf ui.router.util.$urlMatcherFactory
30520            *
30521            * @description
30522            * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
30523            * generate URLs with typed parameters.
30524            *
30525            * @param {string} name  The type name.
30526            * @param {Object|Function} definition   The type definition. See
30527            *        {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30528            * @param {Object|Function} definitionFn (optional) A function that is injected before the app
30529            *        runtime starts.  The result of this function is merged into the existing `definition`.
30530            *        See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30531            *
30532            * @returns {Object}  Returns `$urlMatcherFactoryProvider`.
30533            *
30534            * @example
30535            * This is a simple example of a custom type that encodes and decodes items from an
30536            * array, using the array index as the URL-encoded value:
30537            *
30538            * <pre>
30539            * var list = ['John', 'Paul', 'George', 'Ringo'];
30540            *
30541            * $urlMatcherFactoryProvider.type('listItem', {
30542            *   encode: function(item) {
30543            *     // Represent the list item in the URL using its corresponding index
30544            *     return list.indexOf(item);
30545            *   },
30546            *   decode: function(item) {
30547            *     // Look up the list item by index
30548            *     return list[parseInt(item, 10)];
30549            *   },
30550            *   is: function(item) {
30551            *     // Ensure the item is valid by checking to see that it appears
30552            *     // in the list
30553            *     return list.indexOf(item) > -1;
30554            *   }
30555            * });
30556            *
30557            * $stateProvider.state('list', {
30558            *   url: "/list/{item:listItem}",
30559            *   controller: function($scope, $stateParams) {
30560            *     console.log($stateParams.item);
30561            *   }
30562            * });
30563            *
30564            * // ...
30565            *
30566            * // Changes URL to '/list/3', logs "Ringo" to the console
30567            * $state.go('list', { item: "Ringo" });
30568            * </pre>
30569            *
30570            * This is a more complex example of a type that relies on dependency injection to
30571            * interact with services, and uses the parameter name from the URL to infer how to
30572            * handle encoding and decoding parameter values:
30573            *
30574            * <pre>
30575            * // Defines a custom type that gets a value from a service,
30576            * // where each service gets different types of values from
30577            * // a backend API:
30578            * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
30579            *
30580            *   // Matches up services to URL parameter names
30581            *   var services = {
30582            *     user: Users,
30583            *     post: Posts
30584            *   };
30585            *
30586            *   return {
30587            *     encode: function(object) {
30588            *       // Represent the object in the URL using its unique ID
30589            *       return object.id;
30590            *     },
30591            *     decode: function(value, key) {
30592            *       // Look up the object by ID, using the parameter
30593            *       // name (key) to call the correct service
30594            *       return services[key].findById(value);
30595            *     },
30596            *     is: function(object, key) {
30597            *       // Check that object is a valid dbObject
30598            *       return angular.isObject(object) && object.id && services[key];
30599            *     }
30600            *     equals: function(a, b) {
30601            *       // Check the equality of decoded objects by comparing
30602            *       // their unique IDs
30603            *       return a.id === b.id;
30604            *     }
30605            *   };
30606            * });
30607            *
30608            * // In a config() block, you can then attach URLs with
30609            * // type-annotated parameters:
30610            * $stateProvider.state('users', {
30611            *   url: "/users",
30612            *   // ...
30613            * }).state('users.item', {
30614            *   url: "/{user:dbObject}",
30615            *   controller: function($scope, $stateParams) {
30616            *     // $stateParams.user will now be an object returned from
30617            *     // the Users service
30618            *   },
30619            *   // ...
30620            * });
30621            * </pre>
30622            */
30623           this.type = function (name, definition, definitionFn) {
30624             if (!isDefined(definition)) return $types[name];
30625             if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
30626
30627             $types[name] = new Type(extend({ name: name }, definition));
30628             if (definitionFn) {
30629               typeQueue.push({ name: name, def: definitionFn });
30630               if (!enqueue) flushTypeQueue();
30631             }
30632             return this;
30633           };
30634
30635           // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
30636           function flushTypeQueue() {
30637             while(typeQueue.length) {
30638               var type = typeQueue.shift();
30639               if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
30640               angular.extend($types[type.name], injector.invoke(type.def));
30641             }
30642           }
30643
30644           // Register default types. Store them in the prototype of $types.
30645           forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
30646           $types = inherit($types, {});
30647
30648           /* No need to document $get, since it returns this */
30649           this.$get = ['$injector', function ($injector) {
30650             injector = $injector;
30651             enqueue = false;
30652             flushTypeQueue();
30653
30654             forEach(defaultTypes, function(type, name) {
30655               if (!$types[name]) $types[name] = new Type(type);
30656             });
30657             return this;
30658           }];
30659
30660           this.Param = function Param(id, type, config, location) {
30661             var self = this;
30662             config = unwrapShorthand(config);
30663             type = getType(config, type, location);
30664             var arrayMode = getArrayMode();
30665             type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
30666             if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
30667               config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
30668             var isOptional = config.value !== undefined;
30669             var squash = getSquashPolicy(config, isOptional);
30670             var replace = getReplace(config, arrayMode, isOptional, squash);
30671
30672             function unwrapShorthand(config) {
30673               var keys = isObject(config) ? objectKeys(config) : [];
30674               var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
30675                                 indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
30676               if (isShorthand) config = { value: config };
30677               config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
30678               return config;
30679             }
30680
30681             function getType(config, urlType, location) {
30682               if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
30683               if (urlType) return urlType;
30684               if (!config.type) return (location === "config" ? $types.any : $types.string);
30685               return config.type instanceof Type ? config.type : new Type(config.type);
30686             }
30687
30688             // array config: param name (param[]) overrides default settings.  explicit config overrides param name.
30689             function getArrayMode() {
30690               var arrayDefaults = { array: (location === "search" ? "auto" : false) };
30691               var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
30692               return extend(arrayDefaults, arrayParamNomenclature, config).array;
30693             }
30694
30695             /**
30696              * returns false, true, or the squash value to indicate the "default parameter url squash policy".
30697              */
30698             function getSquashPolicy(config, isOptional) {
30699               var squash = config.squash;
30700               if (!isOptional || squash === false) return false;
30701               if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
30702               if (squash === true || isString(squash)) return squash;
30703               throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
30704             }
30705
30706             function getReplace(config, arrayMode, isOptional, squash) {
30707               var replace, configuredKeys, defaultPolicy = [
30708                 { from: "",   to: (isOptional || arrayMode ? undefined : "") },
30709                 { from: null, to: (isOptional || arrayMode ? undefined : "") }
30710               ];
30711               replace = isArray(config.replace) ? config.replace : [];
30712               if (isString(squash))
30713                 replace.push({ from: squash, to: undefined });
30714               configuredKeys = map(replace, function(item) { return item.from; } );
30715               return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
30716             }
30717
30718             /**
30719              * [Internal] Get the default value of a parameter, which may be an injectable function.
30720              */
30721             function $$getDefaultValue() {
30722               if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30723               var defaultValue = injector.invoke(config.$$fn);
30724               if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
30725                 throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
30726               return defaultValue;
30727             }
30728
30729             /**
30730              * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
30731              * default value, which may be the result of an injectable function.
30732              */
30733             function $value(value) {
30734               function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
30735               function $replace(value) {
30736                 var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
30737                 return replacement.length ? replacement[0] : value;
30738               }
30739               value = $replace(value);
30740               return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
30741             }
30742
30743             function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
30744
30745             extend(this, {
30746               id: id,
30747               type: type,
30748               location: location,
30749               array: arrayMode,
30750               squash: squash,
30751               replace: replace,
30752               isOptional: isOptional,
30753               value: $value,
30754               dynamic: undefined,
30755               config: config,
30756               toString: toString
30757             });
30758           };
30759
30760           function ParamSet(params) {
30761             extend(this, params || {});
30762           }
30763
30764           ParamSet.prototype = {
30765             $$new: function() {
30766               return inherit(this, extend(new ParamSet(), { $$parent: this}));
30767             },
30768             $$keys: function () {
30769               var keys = [], chain = [], parent = this,
30770                 ignore = objectKeys(ParamSet.prototype);
30771               while (parent) { chain.push(parent); parent = parent.$$parent; }
30772               chain.reverse();
30773               forEach(chain, function(paramset) {
30774                 forEach(objectKeys(paramset), function(key) {
30775                     if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
30776                 });
30777               });
30778               return keys;
30779             },
30780             $$values: function(paramValues) {
30781               var values = {}, self = this;
30782               forEach(self.$$keys(), function(key) {
30783                 values[key] = self[key].value(paramValues && paramValues[key]);
30784               });
30785               return values;
30786             },
30787             $$equals: function(paramValues1, paramValues2) {
30788               var equal = true, self = this;
30789               forEach(self.$$keys(), function(key) {
30790                 var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
30791                 if (!self[key].type.equals(left, right)) equal = false;
30792               });
30793               return equal;
30794             },
30795             $$validates: function $$validate(paramValues) {
30796               var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
30797               for (i = 0; i < keys.length; i++) {
30798                 param = this[keys[i]];
30799                 rawVal = paramValues[keys[i]];
30800                 if ((rawVal === undefined || rawVal === null) && param.isOptional)
30801                   break; // There was no parameter value, but the param is optional
30802                 normalized = param.type.$normalize(rawVal);
30803                 if (!param.type.is(normalized))
30804                   return false; // The value was not of the correct Type, and could not be decoded to the correct Type
30805                 encoded = param.type.encode(normalized);
30806                 if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
30807                   return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
30808               }
30809               return true;
30810             },
30811             $$parent: undefined
30812           };
30813
30814           this.ParamSet = ParamSet;
30815         }
30816
30817         // Register as a provider so it's available to other providers
30818         angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
30819         angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
30820
30821         /**
30822          * @ngdoc object
30823          * @name ui.router.router.$urlRouterProvider
30824          *
30825          * @requires ui.router.util.$urlMatcherFactoryProvider
30826          * @requires $locationProvider
30827          *
30828          * @description
30829          * `$urlRouterProvider` has the responsibility of watching `$location`. 
30830          * When `$location` changes it runs through a list of rules one by one until a 
30831          * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 
30832          * a url in a state configuration. All urls are compiled into a UrlMatcher object.
30833          *
30834          * There are several methods on `$urlRouterProvider` that make it useful to use directly
30835          * in your module config.
30836          */
30837         $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
30838         function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
30839           var rules = [], otherwise = null, interceptDeferred = false, listener;
30840
30841           // Returns a string that is a prefix of all strings matching the RegExp
30842           function regExpPrefix(re) {
30843             var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
30844             return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
30845           }
30846
30847           // Interpolates matched values into a String.replace()-style pattern
30848           function interpolate(pattern, match) {
30849             return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30850               return match[what === '$' ? 0 : Number(what)];
30851             });
30852           }
30853
30854           /**
30855            * @ngdoc function
30856            * @name ui.router.router.$urlRouterProvider#rule
30857            * @methodOf ui.router.router.$urlRouterProvider
30858            *
30859            * @description
30860            * Defines rules that are used by `$urlRouterProvider` to find matches for
30861            * specific URLs.
30862            *
30863            * @example
30864            * <pre>
30865            * var app = angular.module('app', ['ui.router.router']);
30866            *
30867            * app.config(function ($urlRouterProvider) {
30868            *   // Here's an example of how you might allow case insensitive urls
30869            *   $urlRouterProvider.rule(function ($injector, $location) {
30870            *     var path = $location.path(),
30871            *         normalized = path.toLowerCase();
30872            *
30873            *     if (path !== normalized) {
30874            *       return normalized;
30875            *     }
30876            *   });
30877            * });
30878            * </pre>
30879            *
30880            * @param {object} rule Handler function that takes `$injector` and `$location`
30881            * services as arguments. You can use them to return a valid path as a string.
30882            *
30883            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30884            */
30885           this.rule = function (rule) {
30886             if (!isFunction(rule)) throw new Error("'rule' must be a function");
30887             rules.push(rule);
30888             return this;
30889           };
30890
30891           /**
30892            * @ngdoc object
30893            * @name ui.router.router.$urlRouterProvider#otherwise
30894            * @methodOf ui.router.router.$urlRouterProvider
30895            *
30896            * @description
30897            * Defines a path that is used when an invalid route is requested.
30898            *
30899            * @example
30900            * <pre>
30901            * var app = angular.module('app', ['ui.router.router']);
30902            *
30903            * app.config(function ($urlRouterProvider) {
30904            *   // if the path doesn't match any of the urls you configured
30905            *   // otherwise will take care of routing the user to the
30906            *   // specified url
30907            *   $urlRouterProvider.otherwise('/index');
30908            *
30909            *   // Example of using function rule as param
30910            *   $urlRouterProvider.otherwise(function ($injector, $location) {
30911            *     return '/a/valid/url';
30912            *   });
30913            * });
30914            * </pre>
30915            *
30916            * @param {string|object} rule The url path you want to redirect to or a function 
30917            * rule that returns the url path. The function version is passed two params: 
30918            * `$injector` and `$location` services, and must return a url string.
30919            *
30920            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30921            */
30922           this.otherwise = function (rule) {
30923             if (isString(rule)) {
30924               var redirect = rule;
30925               rule = function () { return redirect; };
30926             }
30927             else if (!isFunction(rule)) throw new Error("'rule' must be a function");
30928             otherwise = rule;
30929             return this;
30930           };
30931
30932
30933           function handleIfMatch($injector, handler, match) {
30934             if (!match) return false;
30935             var result = $injector.invoke(handler, handler, { $match: match });
30936             return isDefined(result) ? result : true;
30937           }
30938
30939           /**
30940            * @ngdoc function
30941            * @name ui.router.router.$urlRouterProvider#when
30942            * @methodOf ui.router.router.$urlRouterProvider
30943            *
30944            * @description
30945            * Registers a handler for a given url matching. if handle is a string, it is
30946            * treated as a redirect, and is interpolated according to the syntax of match
30947            * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
30948            *
30949            * If the handler is a function, it is injectable. It gets invoked if `$location`
30950            * matches. You have the option of inject the match object as `$match`.
30951            *
30952            * The handler can return
30953            *
30954            * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
30955            *   will continue trying to find another one that matches.
30956            * - **string** which is treated as a redirect and passed to `$location.url()`
30957            * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
30958            *
30959            * @example
30960            * <pre>
30961            * var app = angular.module('app', ['ui.router.router']);
30962            *
30963            * app.config(function ($urlRouterProvider) {
30964            *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
30965            *     if ($state.$current.navigable !== state ||
30966            *         !equalForKeys($match, $stateParams) {
30967            *      $state.transitionTo(state, $match, false);
30968            *     }
30969            *   });
30970            * });
30971            * </pre>
30972            *
30973            * @param {string|object} what The incoming path that you want to redirect.
30974            * @param {string|object} handler The path you want to redirect your user to.
30975            */
30976           this.when = function (what, handler) {
30977             var redirect, handlerIsString = isString(handler);
30978             if (isString(what)) what = $urlMatcherFactory.compile(what);
30979
30980             if (!handlerIsString && !isFunction(handler) && !isArray(handler))
30981               throw new Error("invalid 'handler' in when()");
30982
30983             var strategies = {
30984               matcher: function (what, handler) {
30985                 if (handlerIsString) {
30986                   redirect = $urlMatcherFactory.compile(handler);
30987                   handler = ['$match', function ($match) { return redirect.format($match); }];
30988                 }
30989                 return extend(function ($injector, $location) {
30990                   return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
30991                 }, {
30992                   prefix: isString(what.prefix) ? what.prefix : ''
30993                 });
30994               },
30995               regex: function (what, handler) {
30996                 if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
30997
30998                 if (handlerIsString) {
30999                   redirect = handler;
31000                   handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
31001                 }
31002                 return extend(function ($injector, $location) {
31003                   return handleIfMatch($injector, handler, what.exec($location.path()));
31004                 }, {
31005                   prefix: regExpPrefix(what)
31006                 });
31007               }
31008             };
31009
31010             var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
31011
31012             for (var n in check) {
31013               if (check[n]) return this.rule(strategies[n](what, handler));
31014             }
31015
31016             throw new Error("invalid 'what' in when()");
31017           };
31018
31019           /**
31020            * @ngdoc function
31021            * @name ui.router.router.$urlRouterProvider#deferIntercept
31022            * @methodOf ui.router.router.$urlRouterProvider
31023            *
31024            * @description
31025            * Disables (or enables) deferring location change interception.
31026            *
31027            * If you wish to customize the behavior of syncing the URL (for example, if you wish to
31028            * defer a transition but maintain the current URL), call this method at configuration time.
31029            * Then, at run time, call `$urlRouter.listen()` after you have configured your own
31030            * `$locationChangeSuccess` event handler.
31031            *
31032            * @example
31033            * <pre>
31034            * var app = angular.module('app', ['ui.router.router']);
31035            *
31036            * app.config(function ($urlRouterProvider) {
31037            *
31038            *   // Prevent $urlRouter from automatically intercepting URL changes;
31039            *   // this allows you to configure custom behavior in between
31040            *   // location changes and route synchronization:
31041            *   $urlRouterProvider.deferIntercept();
31042            *
31043            * }).run(function ($rootScope, $urlRouter, UserService) {
31044            *
31045            *   $rootScope.$on('$locationChangeSuccess', function(e) {
31046            *     // UserService is an example service for managing user state
31047            *     if (UserService.isLoggedIn()) return;
31048            *
31049            *     // Prevent $urlRouter's default handler from firing
31050            *     e.preventDefault();
31051            *
31052            *     UserService.handleLogin().then(function() {
31053            *       // Once the user has logged in, sync the current URL
31054            *       // to the router:
31055            *       $urlRouter.sync();
31056            *     });
31057            *   });
31058            *
31059            *   // Configures $urlRouter's listener *after* your custom listener
31060            *   $urlRouter.listen();
31061            * });
31062            * </pre>
31063            *
31064            * @param {boolean} defer Indicates whether to defer location change interception. Passing
31065                     no parameter is equivalent to `true`.
31066            */
31067           this.deferIntercept = function (defer) {
31068             if (defer === undefined) defer = true;
31069             interceptDeferred = defer;
31070           };
31071
31072           /**
31073            * @ngdoc object
31074            * @name ui.router.router.$urlRouter
31075            *
31076            * @requires $location
31077            * @requires $rootScope
31078            * @requires $injector
31079            * @requires $browser
31080            *
31081            * @description
31082            *
31083            */
31084           this.$get = $get;
31085           $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
31086           function $get(   $location,   $rootScope,   $injector,   $browser) {
31087
31088             var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
31089
31090             function appendBasePath(url, isHtml5, absolute) {
31091               if (baseHref === '/') return url;
31092               if (isHtml5) return baseHref.slice(0, -1) + url;
31093               if (absolute) return baseHref.slice(1) + url;
31094               return url;
31095             }
31096
31097             // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
31098             function update(evt) {
31099               if (evt && evt.defaultPrevented) return;
31100               var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
31101               lastPushedUrl = undefined;
31102               // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
31103               //if (ignoreUpdate) return true;
31104
31105               function check(rule) {
31106                 var handled = rule($injector, $location);
31107
31108                 if (!handled) return false;
31109                 if (isString(handled)) $location.replace().url(handled);
31110                 return true;
31111               }
31112               var n = rules.length, i;
31113
31114               for (i = 0; i < n; i++) {
31115                 if (check(rules[i])) return;
31116               }
31117               // always check otherwise last to allow dynamic updates to the set of rules
31118               if (otherwise) check(otherwise);
31119             }
31120
31121             function listen() {
31122               listener = listener || $rootScope.$on('$locationChangeSuccess', update);
31123               return listener;
31124             }
31125
31126             if (!interceptDeferred) listen();
31127
31128             return {
31129               /**
31130                * @ngdoc function
31131                * @name ui.router.router.$urlRouter#sync
31132                * @methodOf ui.router.router.$urlRouter
31133                *
31134                * @description
31135                * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
31136                * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
31137                * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
31138                * with the transition by calling `$urlRouter.sync()`.
31139                *
31140                * @example
31141                * <pre>
31142                * angular.module('app', ['ui.router'])
31143                *   .run(function($rootScope, $urlRouter) {
31144                *     $rootScope.$on('$locationChangeSuccess', function(evt) {
31145                *       // Halt state change from even starting
31146                *       evt.preventDefault();
31147                *       // Perform custom logic
31148                *       var meetsRequirement = ...
31149                *       // Continue with the update and state transition if logic allows
31150                *       if (meetsRequirement) $urlRouter.sync();
31151                *     });
31152                * });
31153                * </pre>
31154                */
31155               sync: function() {
31156                 update();
31157               },
31158
31159               listen: function() {
31160                 return listen();
31161               },
31162
31163               update: function(read) {
31164                 if (read) {
31165                   location = $location.url();
31166                   return;
31167                 }
31168                 if ($location.url() === location) return;
31169
31170                 $location.url(location);
31171                 $location.replace();
31172               },
31173
31174               push: function(urlMatcher, params, options) {
31175                  var url = urlMatcher.format(params || {});
31176
31177                 // Handle the special hash param, if needed
31178                 if (url !== null && params && params['#']) {
31179                     url += '#' + params['#'];
31180                 }
31181
31182                 $location.url(url);
31183                 lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
31184                 if (options && options.replace) $location.replace();
31185               },
31186
31187               /**
31188                * @ngdoc function
31189                * @name ui.router.router.$urlRouter#href
31190                * @methodOf ui.router.router.$urlRouter
31191                *
31192                * @description
31193                * A URL generation method that returns the compiled URL for a given
31194                * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
31195                *
31196                * @example
31197                * <pre>
31198                * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
31199                *   person: "bob"
31200                * });
31201                * // $bob == "/about/bob";
31202                * </pre>
31203                *
31204                * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
31205                * @param {object=} params An object of parameter values to fill the matcher's required parameters.
31206                * @param {object=} options Options object. The options are:
31207                *
31208                * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
31209                *
31210                * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
31211                */
31212               href: function(urlMatcher, params, options) {
31213                 if (!urlMatcher.validates(params)) return null;
31214
31215                 var isHtml5 = $locationProvider.html5Mode();
31216                 if (angular.isObject(isHtml5)) {
31217                   isHtml5 = isHtml5.enabled;
31218                 }
31219                 
31220                 var url = urlMatcher.format(params);
31221                 options = options || {};
31222
31223                 if (!isHtml5 && url !== null) {
31224                   url = "#" + $locationProvider.hashPrefix() + url;
31225                 }
31226
31227                 // Handle special hash param, if needed
31228                 if (url !== null && params && params['#']) {
31229                   url += '#' + params['#'];
31230                 }
31231
31232                 url = appendBasePath(url, isHtml5, options.absolute);
31233
31234                 if (!options.absolute || !url) {
31235                   return url;
31236                 }
31237
31238                 var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
31239                 port = (port === 80 || port === 443 ? '' : ':' + port);
31240
31241                 return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
31242               }
31243             };
31244           }
31245         }
31246
31247         angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
31248
31249         /**
31250          * @ngdoc object
31251          * @name ui.router.state.$stateProvider
31252          *
31253          * @requires ui.router.router.$urlRouterProvider
31254          * @requires ui.router.util.$urlMatcherFactoryProvider
31255          *
31256          * @description
31257          * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
31258          * on state.
31259          *
31260          * A state corresponds to a "place" in the application in terms of the overall UI and
31261          * navigation. A state describes (via the controller / template / view properties) what
31262          * the UI looks like and does at that place.
31263          *
31264          * States often have things in common, and the primary way of factoring out these
31265          * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
31266          * nested states.
31267          *
31268          * The `$stateProvider` provides interfaces to declare these states for your app.
31269          */
31270         $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
31271         function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
31272
31273           var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
31274
31275           // Builds state properties from definition passed to registerState()
31276           var stateBuilder = {
31277
31278             // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31279             // state.children = [];
31280             // if (parent) parent.children.push(state);
31281             parent: function(state) {
31282               if (isDefined(state.parent) && state.parent) return findState(state.parent);
31283               // regex matches any valid composite state name
31284               // would match "contact.list" but not "contacts"
31285               var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
31286               return compositeName ? findState(compositeName[1]) : root;
31287             },
31288
31289             // inherit 'data' from parent and override by own values (if any)
31290             data: function(state) {
31291               if (state.parent && state.parent.data) {
31292                 state.data = state.self.data = extend({}, state.parent.data, state.data);
31293               }
31294               return state.data;
31295             },
31296
31297             // Build a URLMatcher if necessary, either via a relative or absolute URL
31298             url: function(state) {
31299               var url = state.url, config = { params: state.params || {} };
31300
31301               if (isString(url)) {
31302                 if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
31303                 return (state.parent.navigable || root).url.concat(url, config);
31304               }
31305
31306               if (!url || $urlMatcherFactory.isMatcher(url)) return url;
31307               throw new Error("Invalid url '" + url + "' in state '" + state + "'");
31308             },
31309
31310             // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
31311             navigable: function(state) {
31312               return state.url ? state : (state.parent ? state.parent.navigable : null);
31313             },
31314
31315             // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
31316             ownParams: function(state) {
31317               var params = state.url && state.url.params || new $$UMFP.ParamSet();
31318               forEach(state.params || {}, function(config, id) {
31319                 if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
31320               });
31321               return params;
31322             },
31323
31324             // Derive parameters for this state and ensure they're a super-set of parent's parameters
31325             params: function(state) {
31326               return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
31327             },
31328
31329             // If there is no explicit multi-view configuration, make one up so we don't have
31330             // to handle both cases in the view directive later. Note that having an explicit
31331             // 'views' property will mean the default unnamed view properties are ignored. This
31332             // is also a good time to resolve view names to absolute names, so everything is a
31333             // straight lookup at link time.
31334             views: function(state) {
31335               var views = {};
31336
31337               forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
31338                 if (name.indexOf('@') < 0) name += '@' + state.parent.name;
31339                 views[name] = view;
31340               });
31341               return views;
31342             },
31343
31344             // Keep a full path from the root down to this state as this is needed for state activation.
31345             path: function(state) {
31346               return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
31347             },
31348
31349             // Speed up $state.contains() as it's used a lot
31350             includes: function(state) {
31351               var includes = state.parent ? extend({}, state.parent.includes) : {};
31352               includes[state.name] = true;
31353               return includes;
31354             },
31355
31356             $delegates: {}
31357           };
31358
31359           function isRelative(stateName) {
31360             return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
31361           }
31362
31363           function findState(stateOrName, base) {
31364             if (!stateOrName) return undefined;
31365
31366             var isStr = isString(stateOrName),
31367                 name  = isStr ? stateOrName : stateOrName.name,
31368                 path  = isRelative(name);
31369
31370             if (path) {
31371               if (!base) throw new Error("No reference point given for path '"  + name + "'");
31372               base = findState(base);
31373               
31374               var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
31375
31376               for (; i < pathLength; i++) {
31377                 if (rel[i] === "" && i === 0) {
31378                   current = base;
31379                   continue;
31380                 }
31381                 if (rel[i] === "^") {
31382                   if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
31383                   current = current.parent;
31384                   continue;
31385                 }
31386                 break;
31387               }
31388               rel = rel.slice(i).join(".");
31389               name = current.name + (current.name && rel ? "." : "") + rel;
31390             }
31391             var state = states[name];
31392
31393             if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
31394               return state;
31395             }
31396             return undefined;
31397           }
31398
31399           function queueState(parentName, state) {
31400             if (!queue[parentName]) {
31401               queue[parentName] = [];
31402             }
31403             queue[parentName].push(state);
31404           }
31405
31406           function flushQueuedChildren(parentName) {
31407             var queued = queue[parentName] || [];
31408             while(queued.length) {
31409               registerState(queued.shift());
31410             }
31411           }
31412
31413           function registerState(state) {
31414             // Wrap a new object around the state so we can store our private details easily.
31415             state = inherit(state, {
31416               self: state,
31417               resolve: state.resolve || {},
31418               toString: function() { return this.name; }
31419             });
31420
31421             var name = state.name;
31422             if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
31423             if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
31424
31425             // Get parent name
31426             var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
31427                 : (isString(state.parent)) ? state.parent
31428                 : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
31429                 : '';
31430
31431             // If parent is not registered yet, add state to queue and register later
31432             if (parentName && !states[parentName]) {
31433               return queueState(parentName, state.self);
31434             }
31435
31436             for (var key in stateBuilder) {
31437               if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
31438             }
31439             states[name] = state;
31440
31441             // Register the state in the global state list and with $urlRouter if necessary.
31442             if (!state[abstractKey] && state.url) {
31443               $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
31444                 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
31445                   $state.transitionTo(state, $match, { inherit: true, location: false });
31446                 }
31447               }]);
31448             }
31449
31450             // Register any queued children
31451             flushQueuedChildren(name);
31452
31453             return state;
31454           }
31455
31456           // Checks text to see if it looks like a glob.
31457           function isGlob (text) {
31458             return text.indexOf('*') > -1;
31459           }
31460
31461           // Returns true if glob matches current $state name.
31462           function doesStateMatchGlob (glob) {
31463             var globSegments = glob.split('.'),
31464                 segments = $state.$current.name.split('.');
31465
31466             //match single stars
31467             for (var i = 0, l = globSegments.length; i < l; i++) {
31468               if (globSegments[i] === '*') {
31469                 segments[i] = '*';
31470               }
31471             }
31472
31473             //match greedy starts
31474             if (globSegments[0] === '**') {
31475                segments = segments.slice(indexOf(segments, globSegments[1]));
31476                segments.unshift('**');
31477             }
31478             //match greedy ends
31479             if (globSegments[globSegments.length - 1] === '**') {
31480                segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
31481                segments.push('**');
31482             }
31483
31484             if (globSegments.length != segments.length) {
31485               return false;
31486             }
31487
31488             return segments.join('') === globSegments.join('');
31489           }
31490
31491
31492           // Implicit root state that is always active
31493           root = registerState({
31494             name: '',
31495             url: '^',
31496             views: null,
31497             'abstract': true
31498           });
31499           root.navigable = null;
31500
31501
31502           /**
31503            * @ngdoc function
31504            * @name ui.router.state.$stateProvider#decorator
31505            * @methodOf ui.router.state.$stateProvider
31506            *
31507            * @description
31508            * Allows you to extend (carefully) or override (at your own peril) the 
31509            * `stateBuilder` object used internally by `$stateProvider`. This can be used 
31510            * to add custom functionality to ui-router, for example inferring templateUrl 
31511            * based on the state name.
31512            *
31513            * When passing only a name, it returns the current (original or decorated) builder
31514            * function that matches `name`.
31515            *
31516            * The builder functions that can be decorated are listed below. Though not all
31517            * necessarily have a good use case for decoration, that is up to you to decide.
31518            *
31519            * In addition, users can attach custom decorators, which will generate new 
31520            * properties within the state's internal definition. There is currently no clear 
31521            * use-case for this beyond accessing internal states (i.e. $state.$current), 
31522            * however, expect this to become increasingly relevant as we introduce additional 
31523            * meta-programming features.
31524            *
31525            * **Warning**: Decorators should not be interdependent because the order of 
31526            * execution of the builder functions in non-deterministic. Builder functions 
31527            * should only be dependent on the state definition object and super function.
31528            *
31529            *
31530            * Existing builder functions and current return values:
31531            *
31532            * - **parent** `{object}` - returns the parent state object.
31533            * - **data** `{object}` - returns state data, including any inherited data that is not
31534            *   overridden by own values (if any).
31535            * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
31536            *   or `null`.
31537            * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is 
31538            *   navigable).
31539            * - **params** `{object}` - returns an array of state params that are ensured to 
31540            *   be a super-set of parent's params.
31541            * - **views** `{object}` - returns a views object where each key is an absolute view 
31542            *   name (i.e. "viewName@stateName") and each value is the config object 
31543            *   (template, controller) for the view. Even when you don't use the views object 
31544            *   explicitly on a state config, one is still created for you internally.
31545            *   So by decorating this builder function you have access to decorating template 
31546            *   and controller properties.
31547            * - **ownParams** `{object}` - returns an array of params that belong to the state, 
31548            *   not including any params defined by ancestor states.
31549            * - **path** `{string}` - returns the full path from the root down to this state. 
31550            *   Needed for state activation.
31551            * - **includes** `{object}` - returns an object that includes every state that 
31552            *   would pass a `$state.includes()` test.
31553            *
31554            * @example
31555            * <pre>
31556            * // Override the internal 'views' builder with a function that takes the state
31557            * // definition, and a reference to the internal function being overridden:
31558            * $stateProvider.decorator('views', function (state, parent) {
31559            *   var result = {},
31560            *       views = parent(state);
31561            *
31562            *   angular.forEach(views, function (config, name) {
31563            *     var autoName = (state.name + '.' + name).replace('.', '/');
31564            *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
31565            *     result[name] = config;
31566            *   });
31567            *   return result;
31568            * });
31569            *
31570            * $stateProvider.state('home', {
31571            *   views: {
31572            *     'contact.list': { controller: 'ListController' },
31573            *     'contact.item': { controller: 'ItemController' }
31574            *   }
31575            * });
31576            *
31577            * // ...
31578            *
31579            * $state.go('home');
31580            * // Auto-populates list and item views with /partials/home/contact/list.html,
31581            * // and /partials/home/contact/item.html, respectively.
31582            * </pre>
31583            *
31584            * @param {string} name The name of the builder function to decorate. 
31585            * @param {object} func A function that is responsible for decorating the original 
31586            * builder function. The function receives two parameters:
31587            *
31588            *   - `{object}` - state - The state config object.
31589            *   - `{object}` - super - The original builder function.
31590            *
31591            * @return {object} $stateProvider - $stateProvider instance
31592            */
31593           this.decorator = decorator;
31594           function decorator(name, func) {
31595             /*jshint validthis: true */
31596             if (isString(name) && !isDefined(func)) {
31597               return stateBuilder[name];
31598             }
31599             if (!isFunction(func) || !isString(name)) {
31600               return this;
31601             }
31602             if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
31603               stateBuilder.$delegates[name] = stateBuilder[name];
31604             }
31605             stateBuilder[name] = func;
31606             return this;
31607           }
31608
31609           /**
31610            * @ngdoc function
31611            * @name ui.router.state.$stateProvider#state
31612            * @methodOf ui.router.state.$stateProvider
31613            *
31614            * @description
31615            * Registers a state configuration under a given state name. The stateConfig object
31616            * has the following acceptable properties.
31617            *
31618            * @param {string} name A unique state name, e.g. "home", "about", "contacts".
31619            * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
31620            * @param {object} stateConfig State configuration object.
31621            * @param {string|function=} stateConfig.template
31622            * <a id='template'></a>
31623            *   html template as a string or a function that returns
31624            *   an html template as a string which should be used by the uiView directives. This property 
31625            *   takes precedence over templateUrl.
31626            *   
31627            *   If `template` is a function, it will be called with the following parameters:
31628            *
31629            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
31630            *     applying the current state
31631            *
31632            * <pre>template:
31633            *   "<h1>inline template definition</h1>" +
31634            *   "<div ui-view></div>"</pre>
31635            * <pre>template: function(params) {
31636            *       return "<h1>generated template</h1>"; }</pre>
31637            * </div>
31638            *
31639            * @param {string|function=} stateConfig.templateUrl
31640            * <a id='templateUrl'></a>
31641            *
31642            *   path or function that returns a path to an html
31643            *   template that should be used by uiView.
31644            *   
31645            *   If `templateUrl` is a function, it will be called with the following parameters:
31646            *
31647            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by 
31648            *     applying the current state
31649            *
31650            * <pre>templateUrl: "home.html"</pre>
31651            * <pre>templateUrl: function(params) {
31652            *     return myTemplates[params.pageId]; }</pre>
31653            *
31654            * @param {function=} stateConfig.templateProvider
31655            * <a id='templateProvider'></a>
31656            *    Provider function that returns HTML content string.
31657            * <pre> templateProvider:
31658            *       function(MyTemplateService, params) {
31659            *         return MyTemplateService.getTemplate(params.pageId);
31660            *       }</pre>
31661            *
31662            * @param {string|function=} stateConfig.controller
31663            * <a id='controller'></a>
31664            *
31665            *  Controller fn that should be associated with newly
31666            *   related scope or the name of a registered controller if passed as a string.
31667            *   Optionally, the ControllerAs may be declared here.
31668            * <pre>controller: "MyRegisteredController"</pre>
31669            * <pre>controller:
31670            *     "MyRegisteredController as fooCtrl"}</pre>
31671            * <pre>controller: function($scope, MyService) {
31672            *     $scope.data = MyService.getData(); }</pre>
31673            *
31674            * @param {function=} stateConfig.controllerProvider
31675            * <a id='controllerProvider'></a>
31676            *
31677            * Injectable provider function that returns the actual controller or string.
31678            * <pre>controllerProvider:
31679            *   function(MyResolveData) {
31680            *     if (MyResolveData.foo)
31681            *       return "FooCtrl"
31682            *     else if (MyResolveData.bar)
31683            *       return "BarCtrl";
31684            *     else return function($scope) {
31685            *       $scope.baz = "Qux";
31686            *     }
31687            *   }</pre>
31688            *
31689            * @param {string=} stateConfig.controllerAs
31690            * <a id='controllerAs'></a>
31691            * 
31692            * A controller alias name. If present the controller will be
31693            *   published to scope under the controllerAs name.
31694            * <pre>controllerAs: "myCtrl"</pre>
31695            *
31696            * @param {string|object=} stateConfig.parent
31697            * <a id='parent'></a>
31698            * Optionally specifies the parent state of this state.
31699            *
31700            * <pre>parent: 'parentState'</pre>
31701            * <pre>parent: parentState // JS variable</pre>
31702            *
31703            * @param {object=} stateConfig.resolve
31704            * <a id='resolve'></a>
31705            *
31706            * An optional map&lt;string, function&gt; of dependencies which
31707            *   should be injected into the controller. If any of these dependencies are promises, 
31708            *   the router will wait for them all to be resolved before the controller is instantiated.
31709            *   If all the promises are resolved successfully, the $stateChangeSuccess event is fired
31710            *   and the values of the resolved promises are injected into any controllers that reference them.
31711            *   If any  of the promises are rejected the $stateChangeError event is fired.
31712            *
31713            *   The map object is:
31714            *   
31715            *   - key - {string}: name of dependency to be injected into controller
31716            *   - factory - {string|function}: If string then it is alias for service. Otherwise if function, 
31717            *     it is injected and return value it treated as dependency. If result is a promise, it is 
31718            *     resolved before its value is injected into controller.
31719            *
31720            * <pre>resolve: {
31721            *     myResolve1:
31722            *       function($http, $stateParams) {
31723            *         return $http.get("/api/foos/"+stateParams.fooID);
31724            *       }
31725            *     }</pre>
31726            *
31727            * @param {string=} stateConfig.url
31728            * <a id='url'></a>
31729            *
31730            *   A url fragment with optional parameters. When a state is navigated or
31731            *   transitioned to, the `$stateParams` service will be populated with any 
31732            *   parameters that were passed.
31733            *
31734            *   (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
31735            *   more details on acceptable patterns )
31736            *
31737            * examples:
31738            * <pre>url: "/home"
31739            * url: "/users/:userid"
31740            * url: "/books/{bookid:[a-zA-Z_-]}"
31741            * url: "/books/{categoryid:int}"
31742            * url: "/books/{publishername:string}/{categoryid:int}"
31743            * url: "/messages?before&after"
31744            * url: "/messages?{before:date}&{after:date}"
31745            * url: "/messages/:mailboxid?{before:date}&{after:date}"
31746            * </pre>
31747            *
31748            * @param {object=} stateConfig.views
31749            * <a id='views'></a>
31750            * an optional map&lt;string, object&gt; which defined multiple views, or targets views
31751            * manually/explicitly.
31752            *
31753            * Examples:
31754            *
31755            * Targets three named `ui-view`s in the parent state's template
31756            * <pre>views: {
31757            *     header: {
31758            *       controller: "headerCtrl",
31759            *       templateUrl: "header.html"
31760            *     }, body: {
31761            *       controller: "bodyCtrl",
31762            *       templateUrl: "body.html"
31763            *     }, footer: {
31764            *       controller: "footCtrl",
31765            *       templateUrl: "footer.html"
31766            *     }
31767            *   }</pre>
31768            *
31769            * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
31770            * <pre>views: {
31771            *     'header@top': {
31772            *       controller: "msgHeaderCtrl",
31773            *       templateUrl: "msgHeader.html"
31774            *     }, 'body': {
31775            *       controller: "messagesCtrl",
31776            *       templateUrl: "messages.html"
31777            *     }
31778            *   }</pre>
31779            *
31780            * @param {boolean=} [stateConfig.abstract=false]
31781            * <a id='abstract'></a>
31782            * An abstract state will never be directly activated,
31783            *   but can provide inherited properties to its common children states.
31784            * <pre>abstract: true</pre>
31785            *
31786            * @param {function=} stateConfig.onEnter
31787            * <a id='onEnter'></a>
31788            *
31789            * Callback function for when a state is entered. Good way
31790            *   to trigger an action or dispatch an event, such as opening a dialog.
31791            * If minifying your scripts, make sure to explictly annotate this function,
31792            * because it won't be automatically annotated by your build tools.
31793            *
31794            * <pre>onEnter: function(MyService, $stateParams) {
31795            *     MyService.foo($stateParams.myParam);
31796            * }</pre>
31797            *
31798            * @param {function=} stateConfig.onExit
31799            * <a id='onExit'></a>
31800            *
31801            * Callback function for when a state is exited. Good way to
31802            *   trigger an action or dispatch an event, such as opening a dialog.
31803            * If minifying your scripts, make sure to explictly annotate this function,
31804            * because it won't be automatically annotated by your build tools.
31805            *
31806            * <pre>onExit: function(MyService, $stateParams) {
31807            *     MyService.cleanup($stateParams.myParam);
31808            * }</pre>
31809            *
31810            * @param {boolean=} [stateConfig.reloadOnSearch=true]
31811            * <a id='reloadOnSearch'></a>
31812            *
31813            * If `false`, will not retrigger the same state
31814            *   just because a search/query parameter has changed (via $location.search() or $location.hash()). 
31815            *   Useful for when you'd like to modify $location.search() without triggering a reload.
31816            * <pre>reloadOnSearch: false</pre>
31817            *
31818            * @param {object=} stateConfig.data
31819            * <a id='data'></a>
31820            *
31821            * Arbitrary data object, useful for custom configuration.  The parent state's `data` is
31822            *   prototypally inherited.  In other words, adding a data property to a state adds it to
31823            *   the entire subtree via prototypal inheritance.
31824            *
31825            * <pre>data: {
31826            *     requiredRole: 'foo'
31827            * } </pre>
31828            *
31829            * @param {object=} stateConfig.params
31830            * <a id='params'></a>
31831            *
31832            * A map which optionally configures parameters declared in the `url`, or
31833            *   defines additional non-url parameters.  For each parameter being
31834            *   configured, add a configuration object keyed to the name of the parameter.
31835            *
31836            *   Each parameter configuration object may contain the following properties:
31837            *
31838            *   - ** value ** - {object|function=}: specifies the default value for this
31839            *     parameter.  This implicitly sets this parameter as optional.
31840            *
31841            *     When UI-Router routes to a state and no value is
31842            *     specified for this parameter in the URL or transition, the
31843            *     default value will be used instead.  If `value` is a function,
31844            *     it will be injected and invoked, and the return value used.
31845            *
31846            *     *Note*: `undefined` is treated as "no default value" while `null`
31847            *     is treated as "the default value is `null`".
31848            *
31849            *     *Shorthand*: If you only need to configure the default value of the
31850            *     parameter, you may use a shorthand syntax.   In the **`params`**
31851            *     map, instead mapping the param name to a full parameter configuration
31852            *     object, simply set map it to the default parameter value, e.g.:
31853            *
31854            * <pre>// define a parameter's default value
31855            * params: {
31856            *     param1: { value: "defaultValue" }
31857            * }
31858            * // shorthand default values
31859            * params: {
31860            *     param1: "defaultValue",
31861            *     param2: "param2Default"
31862            * }</pre>
31863            *
31864            *   - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
31865            *     treated as an array of values.  If you specified a Type, the value will be
31866            *     treated as an array of the specified Type.  Note: query parameter values
31867            *     default to a special `"auto"` mode.
31868            *
31869            *     For query parameters in `"auto"` mode, if multiple  values for a single parameter
31870            *     are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
31871            *     are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`).  However, if
31872            *     only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
31873            *     value (e.g.: `{ foo: '1' }`).
31874            *
31875            * <pre>params: {
31876            *     param1: { array: true }
31877            * }</pre>
31878            *
31879            *   - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
31880            *     the current parameter value is the same as the default value. If `squash` is not set, it uses the
31881            *     configured default squash policy.
31882            *     (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
31883            *
31884            *   There are three squash settings:
31885            *
31886            *     - false: The parameter's default value is not squashed.  It is encoded and included in the URL
31887            *     - true: The parameter's default value is omitted from the URL.  If the parameter is preceeded and followed
31888            *       by slashes in the state's `url` declaration, then one of those slashes are omitted.
31889            *       This can allow for cleaner looking URLs.
31890            *     - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of  your choice.
31891            *
31892            * <pre>params: {
31893            *     param1: {
31894            *       value: "defaultId",
31895            *       squash: true
31896            * } }
31897            * // squash "defaultValue" to "~"
31898            * params: {
31899            *     param1: {
31900            *       value: "defaultValue",
31901            *       squash: "~"
31902            * } }
31903            * </pre>
31904            *
31905            *
31906            * @example
31907            * <pre>
31908            * // Some state name examples
31909            *
31910            * // stateName can be a single top-level name (must be unique).
31911            * $stateProvider.state("home", {});
31912            *
31913            * // Or it can be a nested state name. This state is a child of the
31914            * // above "home" state.
31915            * $stateProvider.state("home.newest", {});
31916            *
31917            * // Nest states as deeply as needed.
31918            * $stateProvider.state("home.newest.abc.xyz.inception", {});
31919            *
31920            * // state() returns $stateProvider, so you can chain state declarations.
31921            * $stateProvider
31922            *   .state("home", {})
31923            *   .state("about", {})
31924            *   .state("contacts", {});
31925            * </pre>
31926            *
31927            */
31928           this.state = state;
31929           function state(name, definition) {
31930             /*jshint validthis: true */
31931             if (isObject(name)) definition = name;
31932             else definition.name = name;
31933             registerState(definition);
31934             return this;
31935           }
31936
31937           /**
31938            * @ngdoc object
31939            * @name ui.router.state.$state
31940            *
31941            * @requires $rootScope
31942            * @requires $q
31943            * @requires ui.router.state.$view
31944            * @requires $injector
31945            * @requires ui.router.util.$resolve
31946            * @requires ui.router.state.$stateParams
31947            * @requires ui.router.router.$urlRouter
31948            *
31949            * @property {object} params A param object, e.g. {sectionId: section.id)}, that 
31950            * you'd like to test against the current active state.
31951            * @property {object} current A reference to the state's config object. However 
31952            * you passed it in. Useful for accessing custom data.
31953            * @property {object} transition Currently pending transition. A promise that'll 
31954            * resolve or reject.
31955            *
31956            * @description
31957            * `$state` service is responsible for representing states as well as transitioning
31958            * between them. It also provides interfaces to ask for current state or even states
31959            * you're coming from.
31960            */
31961           this.$get = $get;
31962           $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
31963           function $get(   $rootScope,   $q,   $view,   $injector,   $resolve,   $stateParams,   $urlRouter,   $location,   $urlMatcherFactory) {
31964
31965             var TransitionSuperseded = $q.reject(new Error('transition superseded'));
31966             var TransitionPrevented = $q.reject(new Error('transition prevented'));
31967             var TransitionAborted = $q.reject(new Error('transition aborted'));
31968             var TransitionFailed = $q.reject(new Error('transition failed'));
31969
31970             // Handles the case where a state which is the target of a transition is not found, and the user
31971             // can optionally retry or defer the transition
31972             function handleRedirect(redirect, state, params, options) {
31973               /**
31974                * @ngdoc event
31975                * @name ui.router.state.$state#$stateNotFound
31976                * @eventOf ui.router.state.$state
31977                * @eventType broadcast on root scope
31978                * @description
31979                * Fired when a requested state **cannot be found** using the provided state name during transition.
31980                * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
31981                * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
31982                * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
31983                * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
31984                *
31985                * @param {Object} event Event object.
31986                * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
31987                * @param {State} fromState Current state object.
31988                * @param {Object} fromParams Current state params.
31989                *
31990                * @example
31991                *
31992                * <pre>
31993                * // somewhere, assume lazy.state has not been defined
31994                * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
31995                *
31996                * // somewhere else
31997                * $scope.$on('$stateNotFound',
31998                * function(event, unfoundState, fromState, fromParams){
31999                *     console.log(unfoundState.to); // "lazy.state"
32000                *     console.log(unfoundState.toParams); // {a:1, b:2}
32001                *     console.log(unfoundState.options); // {inherit:false} + default options
32002                * })
32003                * </pre>
32004                */
32005               var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
32006
32007               if (evt.defaultPrevented) {
32008                 $urlRouter.update();
32009                 return TransitionAborted;
32010               }
32011
32012               if (!evt.retry) {
32013                 return null;
32014               }
32015
32016               // Allow the handler to return a promise to defer state lookup retry
32017               if (options.$retry) {
32018                 $urlRouter.update();
32019                 return TransitionFailed;
32020               }
32021               var retryTransition = $state.transition = $q.when(evt.retry);
32022
32023               retryTransition.then(function() {
32024                 if (retryTransition !== $state.transition) return TransitionSuperseded;
32025                 redirect.options.$retry = true;
32026                 return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
32027               }, function() {
32028                 return TransitionAborted;
32029               });
32030               $urlRouter.update();
32031
32032               return retryTransition;
32033             }
32034
32035             root.locals = { resolve: null, globals: { $stateParams: {} } };
32036
32037             $state = {
32038               params: {},
32039               current: root.self,
32040               $current: root,
32041               transition: null
32042             };
32043
32044             /**
32045              * @ngdoc function
32046              * @name ui.router.state.$state#reload
32047              * @methodOf ui.router.state.$state
32048              *
32049              * @description
32050              * A method that force reloads the current state. All resolves are re-resolved,
32051              * controllers reinstantiated, and events re-fired.
32052              *
32053              * @example
32054              * <pre>
32055              * var app angular.module('app', ['ui.router']);
32056              *
32057              * app.controller('ctrl', function ($scope, $state) {
32058              *   $scope.reload = function(){
32059              *     $state.reload();
32060              *   }
32061              * });
32062              * </pre>
32063              *
32064              * `reload()` is just an alias for:
32065              * <pre>
32066              * $state.transitionTo($state.current, $stateParams, { 
32067              *   reload: true, inherit: false, notify: true
32068              * });
32069              * </pre>
32070              *
32071              * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
32072              * @example
32073              * <pre>
32074              * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
32075              * //and current state is 'contacts.detail.item'
32076              * var app angular.module('app', ['ui.router']);
32077              *
32078              * app.controller('ctrl', function ($scope, $state) {
32079              *   $scope.reload = function(){
32080              *     //will reload 'contact.detail' and 'contact.detail.item' states
32081              *     $state.reload('contact.detail');
32082              *   }
32083              * });
32084              * </pre>
32085              *
32086              * `reload()` is just an alias for:
32087              * <pre>
32088              * $state.transitionTo($state.current, $stateParams, { 
32089              *   reload: true, inherit: false, notify: true
32090              * });
32091              * </pre>
32092
32093              * @returns {promise} A promise representing the state of the new transition. See
32094              * {@link ui.router.state.$state#methods_go $state.go}.
32095              */
32096             $state.reload = function reload(state) {
32097               return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
32098             };
32099
32100             /**
32101              * @ngdoc function
32102              * @name ui.router.state.$state#go
32103              * @methodOf ui.router.state.$state
32104              *
32105              * @description
32106              * Convenience method for transitioning to a new state. `$state.go` calls 
32107              * `$state.transitionTo` internally but automatically sets options to 
32108              * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. 
32109              * This allows you to easily use an absolute or relative to path and specify 
32110              * only the parameters you'd like to update (while letting unspecified parameters 
32111              * inherit from the currently active ancestor states).
32112              *
32113              * @example
32114              * <pre>
32115              * var app = angular.module('app', ['ui.router']);
32116              *
32117              * app.controller('ctrl', function ($scope, $state) {
32118              *   $scope.changeState = function () {
32119              *     $state.go('contact.detail');
32120              *   };
32121              * });
32122              * </pre>
32123              * <img src='../ngdoc_assets/StateGoExamples.png'/>
32124              *
32125              * @param {string} to Absolute state name or relative state path. Some examples:
32126              *
32127              * - `$state.go('contact.detail')` - will go to the `contact.detail` state
32128              * - `$state.go('^')` - will go to a parent state
32129              * - `$state.go('^.sibling')` - will go to a sibling state
32130              * - `$state.go('.child.grandchild')` - will go to grandchild state
32131              *
32132              * @param {object=} params A map of the parameters that will be sent to the state, 
32133              * will populate $stateParams. Any parameters that are not specified will be inherited from currently 
32134              * defined parameters. This allows, for example, going to a sibling state that shares parameters
32135              * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
32136              * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
32137              * will get you all current parameters, etc.
32138              * @param {object=} options Options object. The options are:
32139              *
32140              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32141              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32142              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32143              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32144              *    defines which state to be relative from.
32145              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32146              * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params 
32147              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32148              *    use this when you want to force a reload when *everything* is the same, including search params.
32149              *
32150              * @returns {promise} A promise representing the state of the new transition.
32151              *
32152              * Possible success values:
32153              *
32154              * - $state.current
32155              *
32156              * <br/>Possible rejection values:
32157              *
32158              * - 'transition superseded' - when a newer transition has been started after this one
32159              * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
32160              * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
32161              *   when a `$stateNotFound` `event.retry` promise errors.
32162              * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
32163              * - *resolve error* - when an error has occurred with a `resolve`
32164              *
32165              */
32166             $state.go = function go(to, params, options) {
32167               return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
32168             };
32169
32170             /**
32171              * @ngdoc function
32172              * @name ui.router.state.$state#transitionTo
32173              * @methodOf ui.router.state.$state
32174              *
32175              * @description
32176              * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
32177              * uses `transitionTo` internally. `$state.go` is recommended in most situations.
32178              *
32179              * @example
32180              * <pre>
32181              * var app = angular.module('app', ['ui.router']);
32182              *
32183              * app.controller('ctrl', function ($scope, $state) {
32184              *   $scope.changeState = function () {
32185              *     $state.transitionTo('contact.detail');
32186              *   };
32187              * });
32188              * </pre>
32189              *
32190              * @param {string} to State name.
32191              * @param {object=} toParams A map of the parameters that will be sent to the state,
32192              * will populate $stateParams.
32193              * @param {object=} options Options object. The options are:
32194              *
32195              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32196              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32197              * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
32198              * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), 
32199              *    defines which state to be relative from.
32200              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32201              * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params 
32202              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32203              *    use this when you want to force a reload when *everything* is the same, including search params.
32204              *    if String, then will reload the state with the name given in reload, and any children.
32205              *    if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
32206              *
32207              * @returns {promise} A promise representing the state of the new transition. See
32208              * {@link ui.router.state.$state#methods_go $state.go}.
32209              */
32210             $state.transitionTo = function transitionTo(to, toParams, options) {
32211               toParams = toParams || {};
32212               options = extend({
32213                 location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
32214               }, options || {});
32215
32216               var from = $state.$current, fromParams = $state.params, fromPath = from.path;
32217               var evt, toState = findState(to, options.relative);
32218
32219               // Store the hash param for later (since it will be stripped out by various methods)
32220               var hash = toParams['#'];
32221
32222               if (!isDefined(toState)) {
32223                 var redirect = { to: to, toParams: toParams, options: options };
32224                 var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
32225
32226                 if (redirectResult) {
32227                   return redirectResult;
32228                 }
32229
32230                 // Always retry once if the $stateNotFound was not prevented
32231                 // (handles either redirect changed or state lazy-definition)
32232                 to = redirect.to;
32233                 toParams = redirect.toParams;
32234                 options = redirect.options;
32235                 toState = findState(to, options.relative);
32236
32237                 if (!isDefined(toState)) {
32238                   if (!options.relative) throw new Error("No such state '" + to + "'");
32239                   throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
32240                 }
32241               }
32242               if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
32243               if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
32244               if (!toState.params.$$validates(toParams)) return TransitionFailed;
32245
32246               toParams = toState.params.$$values(toParams);
32247               to = toState;
32248
32249               var toPath = to.path;
32250
32251               // Starting from the root of the path, keep all levels that haven't changed
32252               var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
32253
32254               if (!options.reload) {
32255                 while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
32256                   locals = toLocals[keep] = state.locals;
32257                   keep++;
32258                   state = toPath[keep];
32259                 }
32260               } else if (isString(options.reload) || isObject(options.reload)) {
32261                 if (isObject(options.reload) && !options.reload.name) {
32262                   throw new Error('Invalid reload state object');
32263                 }
32264                 
32265                 var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
32266                 if (options.reload && !reloadState) {
32267                   throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
32268                 }
32269
32270                 while (state && state === fromPath[keep] && state !== reloadState) {
32271                   locals = toLocals[keep] = state.locals;
32272                   keep++;
32273                   state = toPath[keep];
32274                 }
32275               }
32276
32277               // If we're going to the same state and all locals are kept, we've got nothing to do.
32278               // But clear 'transition', as we still want to cancel any other pending transitions.
32279               // TODO: We may not want to bump 'transition' if we're called from a location change
32280               // that we've initiated ourselves, because we might accidentally abort a legitimate
32281               // transition initiated from code?
32282               if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
32283                 if (hash) toParams['#'] = hash;
32284                 $state.params = toParams;
32285                 copy($state.params, $stateParams);
32286                 if (options.location && to.navigable && to.navigable.url) {
32287                   $urlRouter.push(to.navigable.url, toParams, {
32288                     $$avoidResync: true, replace: options.location === 'replace'
32289                   });
32290                   $urlRouter.update(true);
32291                 }
32292                 $state.transition = null;
32293                 return $q.when($state.current);
32294               }
32295
32296               // Filter parameters before we pass them to event handlers etc.
32297               toParams = filterByKeys(to.params.$$keys(), toParams || {});
32298
32299               // Broadcast start event and cancel the transition if requested
32300               if (options.notify) {
32301                 /**
32302                  * @ngdoc event
32303                  * @name ui.router.state.$state#$stateChangeStart
32304                  * @eventOf ui.router.state.$state
32305                  * @eventType broadcast on root scope
32306                  * @description
32307                  * Fired when the state transition **begins**. You can use `event.preventDefault()`
32308                  * to prevent the transition from happening and then the transition promise will be
32309                  * rejected with a `'transition prevented'` value.
32310                  *
32311                  * @param {Object} event Event object.
32312                  * @param {State} toState The state being transitioned to.
32313                  * @param {Object} toParams The params supplied to the `toState`.
32314                  * @param {State} fromState The current state, pre-transition.
32315                  * @param {Object} fromParams The params supplied to the `fromState`.
32316                  *
32317                  * @example
32318                  *
32319                  * <pre>
32320                  * $rootScope.$on('$stateChangeStart',
32321                  * function(event, toState, toParams, fromState, fromParams){
32322                  *     event.preventDefault();
32323                  *     // transitionTo() promise will be rejected with
32324                  *     // a 'transition prevented' error
32325                  * })
32326                  * </pre>
32327                  */
32328                 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
32329                   $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32330                   $urlRouter.update();
32331                   return TransitionPrevented;
32332                 }
32333               }
32334
32335               // Resolve locals for the remaining states, but don't update any global state just
32336               // yet -- if anything fails to resolve the current state needs to remain untouched.
32337               // We also set up an inheritance chain for the locals here. This allows the view directive
32338               // to quickly look up the correct definition for each view in the current state. Even
32339               // though we create the locals object itself outside resolveState(), it is initially
32340               // empty and gets filled asynchronously. We need to keep track of the promise for the
32341               // (fully resolved) current locals, and pass this down the chain.
32342               var resolved = $q.when(locals);
32343
32344               for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
32345                 locals = toLocals[l] = inherit(locals);
32346                 resolved = resolveState(state, toParams, state === to, resolved, locals, options);
32347               }
32348
32349               // Once everything is resolved, we are ready to perform the actual transition
32350               // and return a promise for the new state. We also keep track of what the
32351               // current promise is, so that we can detect overlapping transitions and
32352               // keep only the outcome of the last transition.
32353               var transition = $state.transition = resolved.then(function () {
32354                 var l, entering, exiting;
32355
32356                 if ($state.transition !== transition) return TransitionSuperseded;
32357
32358                 // Exit 'from' states not kept
32359                 for (l = fromPath.length - 1; l >= keep; l--) {
32360                   exiting = fromPath[l];
32361                   if (exiting.self.onExit) {
32362                     $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
32363                   }
32364                   exiting.locals = null;
32365                 }
32366
32367                 // Enter 'to' states not kept
32368                 for (l = keep; l < toPath.length; l++) {
32369                   entering = toPath[l];
32370                   entering.locals = toLocals[l];
32371                   if (entering.self.onEnter) {
32372                     $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
32373                   }
32374                 }
32375
32376                 // Re-add the saved hash before we start returning things
32377                 if (hash) toParams['#'] = hash;
32378
32379                 // Run it again, to catch any transitions in callbacks
32380                 if ($state.transition !== transition) return TransitionSuperseded;
32381
32382                 // Update globals in $state
32383                 $state.$current = to;
32384                 $state.current = to.self;
32385                 $state.params = toParams;
32386                 copy($state.params, $stateParams);
32387                 $state.transition = null;
32388
32389                 if (options.location && to.navigable) {
32390                   $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
32391                     $$avoidResync: true, replace: options.location === 'replace'
32392                   });
32393                 }
32394
32395                 if (options.notify) {
32396                 /**
32397                  * @ngdoc event
32398                  * @name ui.router.state.$state#$stateChangeSuccess
32399                  * @eventOf ui.router.state.$state
32400                  * @eventType broadcast on root scope
32401                  * @description
32402                  * Fired once the state transition is **complete**.
32403                  *
32404                  * @param {Object} event Event object.
32405                  * @param {State} toState The state being transitioned to.
32406                  * @param {Object} toParams The params supplied to the `toState`.
32407                  * @param {State} fromState The current state, pre-transition.
32408                  * @param {Object} fromParams The params supplied to the `fromState`.
32409                  */
32410                   $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
32411                 }
32412                 $urlRouter.update(true);
32413
32414                 return $state.current;
32415               }, function (error) {
32416                 if ($state.transition !== transition) return TransitionSuperseded;
32417
32418                 $state.transition = null;
32419                 /**
32420                  * @ngdoc event
32421                  * @name ui.router.state.$state#$stateChangeError
32422                  * @eventOf ui.router.state.$state
32423                  * @eventType broadcast on root scope
32424                  * @description
32425                  * Fired when an **error occurs** during transition. It's important to note that if you
32426                  * have any errors in your resolve functions (javascript errors, non-existent services, etc)
32427                  * they will not throw traditionally. You must listen for this $stateChangeError event to
32428                  * catch **ALL** errors.
32429                  *
32430                  * @param {Object} event Event object.
32431                  * @param {State} toState The state being transitioned to.
32432                  * @param {Object} toParams The params supplied to the `toState`.
32433                  * @param {State} fromState The current state, pre-transition.
32434                  * @param {Object} fromParams The params supplied to the `fromState`.
32435                  * @param {Error} error The resolve error object.
32436                  */
32437                 evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
32438
32439                 if (!evt.defaultPrevented) {
32440                     $urlRouter.update();
32441                 }
32442
32443                 return $q.reject(error);
32444               });
32445
32446               return transition;
32447             };
32448
32449             /**
32450              * @ngdoc function
32451              * @name ui.router.state.$state#is
32452              * @methodOf ui.router.state.$state
32453              *
32454              * @description
32455              * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
32456              * but only checks for the full state name. If params is supplied then it will be
32457              * tested for strict equality against the current active params object, so all params
32458              * must match with none missing and no extras.
32459              *
32460              * @example
32461              * <pre>
32462              * $state.$current.name = 'contacts.details.item';
32463              *
32464              * // absolute name
32465              * $state.is('contact.details.item'); // returns true
32466              * $state.is(contactDetailItemStateObject); // returns true
32467              *
32468              * // relative name (. and ^), typically from a template
32469              * // E.g. from the 'contacts.details' template
32470              * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
32471              * </pre>
32472              *
32473              * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
32474              * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
32475              * to test against the current active state.
32476              * @param {object=} options An options object.  The options are:
32477              *
32478              * - **`relative`** - {string|object} -  If `stateOrName` is a relative state name and `options.relative` is set, .is will
32479              * test relative to `options.relative` state (or name).
32480              *
32481              * @returns {boolean} Returns true if it is the state.
32482              */
32483             $state.is = function is(stateOrName, params, options) {
32484               options = extend({ relative: $state.$current }, options || {});
32485               var state = findState(stateOrName, options.relative);
32486
32487               if (!isDefined(state)) { return undefined; }
32488               if ($state.$current !== state) { return false; }
32489               return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
32490             };
32491
32492             /**
32493              * @ngdoc function
32494              * @name ui.router.state.$state#includes
32495              * @methodOf ui.router.state.$state
32496              *
32497              * @description
32498              * A method to determine if the current active state is equal to or is the child of the
32499              * state stateName. If any params are passed then they will be tested for a match as well.
32500              * Not all the parameters need to be passed, just the ones you'd like to test for equality.
32501              *
32502              * @example
32503              * Partial and relative names
32504              * <pre>
32505              * $state.$current.name = 'contacts.details.item';
32506              *
32507              * // Using partial names
32508              * $state.includes("contacts"); // returns true
32509              * $state.includes("contacts.details"); // returns true
32510              * $state.includes("contacts.details.item"); // returns true
32511              * $state.includes("contacts.list"); // returns false
32512              * $state.includes("about"); // returns false
32513              *
32514              * // Using relative names (. and ^), typically from a template
32515              * // E.g. from the 'contacts.details' template
32516              * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
32517              * </pre>
32518              *
32519              * Basic globbing patterns
32520              * <pre>
32521              * $state.$current.name = 'contacts.details.item.url';
32522              *
32523              * $state.includes("*.details.*.*"); // returns true
32524              * $state.includes("*.details.**"); // returns true
32525              * $state.includes("**.item.**"); // returns true
32526              * $state.includes("*.details.item.url"); // returns true
32527              * $state.includes("*.details.*.url"); // returns true
32528              * $state.includes("*.details.*"); // returns false
32529              * $state.includes("item.**"); // returns false
32530              * </pre>
32531              *
32532              * @param {string} stateOrName A partial name, relative name, or glob pattern
32533              * to be searched for within the current state name.
32534              * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
32535              * that you'd like to test against the current active state.
32536              * @param {object=} options An options object.  The options are:
32537              *
32538              * - **`relative`** - {string|object=} -  If `stateOrName` is a relative state reference and `options.relative` is set,
32539              * .includes will test relative to `options.relative` state (or name).
32540              *
32541              * @returns {boolean} Returns true if it does include the state
32542              */
32543             $state.includes = function includes(stateOrName, params, options) {
32544               options = extend({ relative: $state.$current }, options || {});
32545               if (isString(stateOrName) && isGlob(stateOrName)) {
32546                 if (!doesStateMatchGlob(stateOrName)) {
32547                   return false;
32548                 }
32549                 stateOrName = $state.$current.name;
32550               }
32551
32552               var state = findState(stateOrName, options.relative);
32553               if (!isDefined(state)) { return undefined; }
32554               if (!isDefined($state.$current.includes[state.name])) { return false; }
32555               return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
32556             };
32557
32558
32559             /**
32560              * @ngdoc function
32561              * @name ui.router.state.$state#href
32562              * @methodOf ui.router.state.$state
32563              *
32564              * @description
32565              * A url generation method that returns the compiled url for the given state populated with the given params.
32566              *
32567              * @example
32568              * <pre>
32569              * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
32570              * </pre>
32571              *
32572              * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
32573              * @param {object=} params An object of parameter values to fill the state's required parameters.
32574              * @param {object=} options Options object. The options are:
32575              *
32576              * - **`lossy`** - {boolean=true} -  If true, and if there is no url associated with the state provided in the
32577              *    first parameter, then the constructed href url will be built from the first navigable ancestor (aka
32578              *    ancestor with a valid url).
32579              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32580              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32581              *    defines which state to be relative from.
32582              * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
32583              * 
32584              * @returns {string} compiled state url
32585              */
32586             $state.href = function href(stateOrName, params, options) {
32587               options = extend({
32588                 lossy:    true,
32589                 inherit:  true,
32590                 absolute: false,
32591                 relative: $state.$current
32592               }, options || {});
32593
32594               var state = findState(stateOrName, options.relative);
32595
32596               if (!isDefined(state)) return null;
32597               if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
32598               
32599               var nav = (state && options.lossy) ? state.navigable : state;
32600
32601               if (!nav || nav.url === undefined || nav.url === null) {
32602                 return null;
32603               }
32604               return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
32605                 absolute: options.absolute
32606               });
32607             };
32608
32609             /**
32610              * @ngdoc function
32611              * @name ui.router.state.$state#get
32612              * @methodOf ui.router.state.$state
32613              *
32614              * @description
32615              * Returns the state configuration object for any specific state or all states.
32616              *
32617              * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
32618              * the requested state. If not provided, returns an array of ALL state configs.
32619              * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
32620              * @returns {Object|Array} State configuration object or array of all objects.
32621              */
32622             $state.get = function (stateOrName, context) {
32623               if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
32624               var state = findState(stateOrName, context || $state.$current);
32625               return (state && state.self) ? state.self : null;
32626             };
32627
32628             function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
32629               // Make a restricted $stateParams with only the parameters that apply to this state if
32630               // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
32631               // we also need $stateParams to be available for any $injector calls we make during the
32632               // dependency resolution process.
32633               var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
32634               var locals = { $stateParams: $stateParams };
32635
32636               // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
32637               // We're also including $stateParams in this; that way the parameters are restricted
32638               // to the set that should be visible to the state, and are independent of when we update
32639               // the global $state and $stateParams values.
32640               dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
32641               var promises = [dst.resolve.then(function (globals) {
32642                 dst.globals = globals;
32643               })];
32644               if (inherited) promises.push(inherited);
32645
32646               function resolveViews() {
32647                 var viewsPromises = [];
32648
32649                 // Resolve template and dependencies for all views.
32650                 forEach(state.views, function (view, name) {
32651                   var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
32652                   injectables.$template = [ function () {
32653                     return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
32654                   }];
32655
32656                   viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
32657                     // References to the controller (only instantiated at link time)
32658                     if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
32659                       var injectLocals = angular.extend({}, injectables, dst.globals);
32660                       result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
32661                     } else {
32662                       result.$$controller = view.controller;
32663                     }
32664                     // Provide access to the state itself for internal use
32665                     result.$$state = state;
32666                     result.$$controllerAs = view.controllerAs;
32667                     dst[name] = result;
32668                   }));
32669                 });
32670
32671                 return $q.all(viewsPromises).then(function(){
32672                   return dst.globals;
32673                 });
32674               }
32675
32676               // Wait for all the promises and then return the activation object
32677               return $q.all(promises).then(resolveViews).then(function (values) {
32678                 return dst;
32679               });
32680             }
32681
32682             return $state;
32683           }
32684
32685           function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
32686             // Return true if there are no differences in non-search (path/object) params, false if there are differences
32687             function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
32688               // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
32689               function notSearchParam(key) {
32690                 return fromAndToState.params[key].location != "search";
32691               }
32692               var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
32693               var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
32694               var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
32695               return nonQueryParamSet.$$equals(fromParams, toParams);
32696             }
32697
32698             // If reload was not explicitly requested
32699             // and we're transitioning to the same state we're already in
32700             // and    the locals didn't change
32701             //     or they changed in a way that doesn't merit reloading
32702             //        (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
32703             // Then return true.
32704             if (!options.reload && to === from &&
32705               (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
32706               return true;
32707             }
32708           }
32709         }
32710
32711         angular.module('ui.router.state')
32712           .value('$stateParams', {})
32713           .provider('$state', $StateProvider);
32714
32715
32716         $ViewProvider.$inject = [];
32717         function $ViewProvider() {
32718
32719           this.$get = $get;
32720           /**
32721            * @ngdoc object
32722            * @name ui.router.state.$view
32723            *
32724            * @requires ui.router.util.$templateFactory
32725            * @requires $rootScope
32726            *
32727            * @description
32728            *
32729            */
32730           $get.$inject = ['$rootScope', '$templateFactory'];
32731           function $get(   $rootScope,   $templateFactory) {
32732             return {
32733               // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
32734               /**
32735                * @ngdoc function
32736                * @name ui.router.state.$view#load
32737                * @methodOf ui.router.state.$view
32738                *
32739                * @description
32740                *
32741                * @param {string} name name
32742                * @param {object} options option object.
32743                */
32744               load: function load(name, options) {
32745                 var result, defaults = {
32746                   template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
32747                 };
32748                 options = extend(defaults, options);
32749
32750                 if (options.view) {
32751                   result = $templateFactory.fromConfig(options.view, options.params, options.locals);
32752                 }
32753                 if (result && options.notify) {
32754                 /**
32755                  * @ngdoc event
32756                  * @name ui.router.state.$state#$viewContentLoading
32757                  * @eventOf ui.router.state.$view
32758                  * @eventType broadcast on root scope
32759                  * @description
32760                  *
32761                  * Fired once the view **begins loading**, *before* the DOM is rendered.
32762                  *
32763                  * @param {Object} event Event object.
32764                  * @param {Object} viewConfig The view config properties (template, controller, etc).
32765                  *
32766                  * @example
32767                  *
32768                  * <pre>
32769                  * $scope.$on('$viewContentLoading',
32770                  * function(event, viewConfig){
32771                  *     // Access to all the view config properties.
32772                  *     // and one special property 'targetView'
32773                  *     // viewConfig.targetView
32774                  * });
32775                  * </pre>
32776                  */
32777                   $rootScope.$broadcast('$viewContentLoading', options);
32778                 }
32779                 return result;
32780               }
32781             };
32782           }
32783         }
32784
32785         angular.module('ui.router.state').provider('$view', $ViewProvider);
32786
32787         /**
32788          * @ngdoc object
32789          * @name ui.router.state.$uiViewScrollProvider
32790          *
32791          * @description
32792          * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
32793          */
32794         function $ViewScrollProvider() {
32795
32796           var useAnchorScroll = false;
32797
32798           /**
32799            * @ngdoc function
32800            * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
32801            * @methodOf ui.router.state.$uiViewScrollProvider
32802            *
32803            * @description
32804            * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
32805            * scrolling based on the url anchor.
32806            */
32807           this.useAnchorScroll = function () {
32808             useAnchorScroll = true;
32809           };
32810
32811           /**
32812            * @ngdoc object
32813            * @name ui.router.state.$uiViewScroll
32814            *
32815            * @requires $anchorScroll
32816            * @requires $timeout
32817            *
32818            * @description
32819            * When called with a jqLite element, it scrolls the element into view (after a
32820            * `$timeout` so the DOM has time to refresh).
32821            *
32822            * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
32823            * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
32824            */
32825           this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
32826             if (useAnchorScroll) {
32827               return $anchorScroll;
32828             }
32829
32830             return function ($element) {
32831               return $timeout(function () {
32832                 $element[0].scrollIntoView();
32833               }, 0, false);
32834             };
32835           }];
32836         }
32837
32838         angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
32839
32840         /**
32841          * @ngdoc directive
32842          * @name ui.router.state.directive:ui-view
32843          *
32844          * @requires ui.router.state.$state
32845          * @requires $compile
32846          * @requires $controller
32847          * @requires $injector
32848          * @requires ui.router.state.$uiViewScroll
32849          * @requires $document
32850          *
32851          * @restrict ECA
32852          *
32853          * @description
32854          * The ui-view directive tells $state where to place your templates.
32855          *
32856          * @param {string=} name A view name. The name should be unique amongst the other views in the
32857          * same state. You can have views of the same name that live in different states.
32858          *
32859          * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
32860          * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
32861          * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
32862          * scroll ui-view elements into view when they are populated during a state activation.
32863          *
32864          * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
32865          * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
32866          *
32867          * @param {string=} onload Expression to evaluate whenever the view updates.
32868          * 
32869          * @example
32870          * A view can be unnamed or named. 
32871          * <pre>
32872          * <!-- Unnamed -->
32873          * <div ui-view></div> 
32874          * 
32875          * <!-- Named -->
32876          * <div ui-view="viewName"></div>
32877          * </pre>
32878          *
32879          * You can only have one unnamed view within any template (or root html). If you are only using a 
32880          * single view and it is unnamed then you can populate it like so:
32881          * <pre>
32882          * <div ui-view></div> 
32883          * $stateProvider.state("home", {
32884          *   template: "<h1>HELLO!</h1>"
32885          * })
32886          * </pre>
32887          * 
32888          * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
32889          * config property, by name, in this case an empty name:
32890          * <pre>
32891          * $stateProvider.state("home", {
32892          *   views: {
32893          *     "": {
32894          *       template: "<h1>HELLO!</h1>"
32895          *     }
32896          *   }    
32897          * })
32898          * </pre>
32899          * 
32900          * But typically you'll only use the views property if you name your view or have more than one view 
32901          * in the same template. There's not really a compelling reason to name a view if its the only one, 
32902          * but you could if you wanted, like so:
32903          * <pre>
32904          * <div ui-view="main"></div>
32905          * </pre> 
32906          * <pre>
32907          * $stateProvider.state("home", {
32908          *   views: {
32909          *     "main": {
32910          *       template: "<h1>HELLO!</h1>"
32911          *     }
32912          *   }    
32913          * })
32914          * </pre>
32915          * 
32916          * Really though, you'll use views to set up multiple views:
32917          * <pre>
32918          * <div ui-view></div>
32919          * <div ui-view="chart"></div> 
32920          * <div ui-view="data"></div> 
32921          * </pre>
32922          * 
32923          * <pre>
32924          * $stateProvider.state("home", {
32925          *   views: {
32926          *     "": {
32927          *       template: "<h1>HELLO!</h1>"
32928          *     },
32929          *     "chart": {
32930          *       template: "<chart_thing/>"
32931          *     },
32932          *     "data": {
32933          *       template: "<data_thing/>"
32934          *     }
32935          *   }    
32936          * })
32937          * </pre>
32938          *
32939          * Examples for `autoscroll`:
32940          *
32941          * <pre>
32942          * <!-- If autoscroll present with no expression,
32943          *      then scroll ui-view into view -->
32944          * <ui-view autoscroll/>
32945          *
32946          * <!-- If autoscroll present with valid expression,
32947          *      then scroll ui-view into view if expression evaluates to true -->
32948          * <ui-view autoscroll='true'/>
32949          * <ui-view autoscroll='false'/>
32950          * <ui-view autoscroll='scopeVariable'/>
32951          * </pre>
32952          */
32953         $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
32954         function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {
32955
32956           function getService() {
32957             return ($injector.has) ? function(service) {
32958               return $injector.has(service) ? $injector.get(service) : null;
32959             } : function(service) {
32960               try {
32961                 return $injector.get(service);
32962               } catch (e) {
32963                 return null;
32964               }
32965             };
32966           }
32967
32968           var service = getService(),
32969               $animator = service('$animator'),
32970               $animate = service('$animate');
32971
32972           // Returns a set of DOM manipulation functions based on which Angular version
32973           // it should use
32974           function getRenderer(attrs, scope) {
32975             var statics = function() {
32976               return {
32977                 enter: function (element, target, cb) { target.after(element); cb(); },
32978                 leave: function (element, cb) { element.remove(); cb(); }
32979               };
32980             };
32981
32982             if ($animate) {
32983               return {
32984                 enter: function(element, target, cb) {
32985                   var promise = $animate.enter(element, null, target, cb);
32986                   if (promise && promise.then) promise.then(cb);
32987                 },
32988                 leave: function(element, cb) {
32989                   var promise = $animate.leave(element, cb);
32990                   if (promise && promise.then) promise.then(cb);
32991                 }
32992               };
32993             }
32994
32995             if ($animator) {
32996               var animate = $animator && $animator(scope, attrs);
32997
32998               return {
32999                 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
33000                 leave: function(element, cb) { animate.leave(element); cb(); }
33001               };
33002             }
33003
33004             return statics();
33005           }
33006
33007           var directive = {
33008             restrict: 'ECA',
33009             terminal: true,
33010             priority: 400,
33011             transclude: 'element',
33012             compile: function (tElement, tAttrs, $transclude) {
33013               return function (scope, $element, attrs) {
33014                 var previousEl, currentEl, currentScope, latestLocals,
33015                     onloadExp     = attrs.onload || '',
33016                     autoScrollExp = attrs.autoscroll,
33017                     renderer      = getRenderer(attrs, scope);
33018
33019                 scope.$on('$stateChangeSuccess', function() {
33020                   updateView(false);
33021                 });
33022                 scope.$on('$viewContentLoading', function() {
33023                   updateView(false);
33024                 });
33025
33026                 updateView(true);
33027
33028                 function cleanupLastView() {
33029                   if (previousEl) {
33030                     previousEl.remove();
33031                     previousEl = null;
33032                   }
33033
33034                   if (currentScope) {
33035                     currentScope.$destroy();
33036                     currentScope = null;
33037                   }
33038
33039                   if (currentEl) {
33040                     renderer.leave(currentEl, function() {
33041                       previousEl = null;
33042                     });
33043
33044                     previousEl = currentEl;
33045                     currentEl = null;
33046                   }
33047                 }
33048
33049                 function updateView(firstTime) {
33050                   var newScope,
33051                       name            = getUiViewName(scope, attrs, $element, $interpolate),
33052                       previousLocals  = name && $state.$current && $state.$current.locals[name];
33053
33054                   if (!firstTime && previousLocals === latestLocals) return; // nothing to do
33055                   newScope = scope.$new();
33056                   latestLocals = $state.$current.locals[name];
33057
33058                   var clone = $transclude(newScope, function(clone) {
33059                     renderer.enter(clone, $element, function onUiViewEnter() {
33060                       if(currentScope) {
33061                         currentScope.$emit('$viewContentAnimationEnded');
33062                       }
33063
33064                       if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
33065                         $uiViewScroll(clone);
33066                       }
33067                     });
33068                     cleanupLastView();
33069                   });
33070
33071                   currentEl = clone;
33072                   currentScope = newScope;
33073                   /**
33074                    * @ngdoc event
33075                    * @name ui.router.state.directive:ui-view#$viewContentLoaded
33076                    * @eventOf ui.router.state.directive:ui-view
33077                    * @eventType emits on ui-view directive scope
33078                    * @description           *
33079                    * Fired once the view is **loaded**, *after* the DOM is rendered.
33080                    *
33081                    * @param {Object} event Event object.
33082                    */
33083                   currentScope.$emit('$viewContentLoaded');
33084                   currentScope.$eval(onloadExp);
33085                 }
33086               };
33087             }
33088           };
33089
33090           return directive;
33091         }
33092
33093         $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
33094         function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
33095           return {
33096             restrict: 'ECA',
33097             priority: -400,
33098             compile: function (tElement) {
33099               var initial = tElement.html();
33100               return function (scope, $element, attrs) {
33101                 var current = $state.$current,
33102                     name = getUiViewName(scope, attrs, $element, $interpolate),
33103                     locals  = current && current.locals[name];
33104
33105                 if (! locals) {
33106                   return;
33107                 }
33108
33109                 $element.data('$uiView', { name: name, state: locals.$$state });
33110                 $element.html(locals.$template ? locals.$template : initial);
33111
33112                 var link = $compile($element.contents());
33113
33114                 if (locals.$$controller) {
33115                   locals.$scope = scope;
33116                   locals.$element = $element;
33117                   var controller = $controller(locals.$$controller, locals);
33118                   if (locals.$$controllerAs) {
33119                     scope[locals.$$controllerAs] = controller;
33120                   }
33121                   $element.data('$ngControllerController', controller);
33122                   $element.children().data('$ngControllerController', controller);
33123                 }
33124
33125                 link(scope);
33126               };
33127             }
33128           };
33129         }
33130
33131         /**
33132          * Shared ui-view code for both directives:
33133          * Given scope, element, and its attributes, return the view's name
33134          */
33135         function getUiViewName(scope, attrs, element, $interpolate) {
33136           var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
33137           var inherited = element.inheritedData('$uiView');
33138           return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
33139         }
33140
33141         angular.module('ui.router.state').directive('uiView', $ViewDirective);
33142         angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
33143
33144         function parseStateRef(ref, current) {
33145           var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
33146           if (preparsed) ref = current + '(' + preparsed[1] + ')';
33147           parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
33148           if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
33149           return { state: parsed[1], paramExpr: parsed[3] || null };
33150         }
33151
33152         function stateContext(el) {
33153           var stateData = el.parent().inheritedData('$uiView');
33154
33155           if (stateData && stateData.state && stateData.state.name) {
33156             return stateData.state;
33157           }
33158         }
33159
33160         /**
33161          * @ngdoc directive
33162          * @name ui.router.state.directive:ui-sref
33163          *
33164          * @requires ui.router.state.$state
33165          * @requires $timeout
33166          *
33167          * @restrict A
33168          *
33169          * @description
33170          * A directive that binds a link (`<a>` tag) to a state. If the state has an associated 
33171          * URL, the directive will automatically generate & update the `href` attribute via 
33172          * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 
33173          * the link will trigger a state transition with optional parameters. 
33174          *
33175          * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 
33176          * handled natively by the browser.
33177          *
33178          * You can also use relative state paths within ui-sref, just like the relative 
33179          * paths passed to `$state.go()`. You just need to be aware that the path is relative
33180          * to the state that the link lives in, in other words the state that loaded the 
33181          * template containing the link.
33182          *
33183          * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
33184          * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
33185          * and `reload`.
33186          *
33187          * @example
33188          * Here's an example of how you'd use ui-sref and how it would compile. If you have the 
33189          * following template:
33190          * <pre>
33191          * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33192          * 
33193          * <ul>
33194          *     <li ng-repeat="contact in contacts">
33195          *         <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
33196          *     </li>
33197          * </ul>
33198          * </pre>
33199          * 
33200          * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33201          * <pre>
33202          * <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>
33203          * 
33204          * <ul>
33205          *     <li ng-repeat="contact in contacts">
33206          *         <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
33207          *     </li>
33208          *     <li ng-repeat="contact in contacts">
33209          *         <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
33210          *     </li>
33211          *     <li ng-repeat="contact in contacts">
33212          *         <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
33213          *     </li>
33214          * </ul>
33215          *
33216          * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
33217          * </pre>
33218          *
33219          * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
33220          * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33221          */
33222         $StateRefDirective.$inject = ['$state', '$timeout'];
33223         function $StateRefDirective($state, $timeout) {
33224           var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
33225
33226           return {
33227             restrict: 'A',
33228             require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33229             link: function(scope, element, attrs, uiSrefActive) {
33230               var ref = parseStateRef(attrs.uiSref, $state.current.name);
33231               var params = null, url = null, base = stateContext(element) || $state.$current;
33232               // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33233               var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
33234                          'xlink:href' : 'href';
33235               var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
33236               var isForm = element[0].nodeName === "FORM";
33237               var attr = isForm ? "action" : hrefKind, nav = true;
33238
33239               var options = { relative: base, inherit: true };
33240               var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
33241
33242               angular.forEach(allowedOptions, function(option) {
33243                 if (option in optionsOverride) {
33244                   options[option] = optionsOverride[option];
33245                 }
33246               });
33247
33248               var update = function(newVal) {
33249                 if (newVal) params = angular.copy(newVal);
33250                 if (!nav) return;
33251
33252                 newHref = $state.href(ref.state, params, options);
33253
33254                 var activeDirective = uiSrefActive[1] || uiSrefActive[0];
33255                 if (activeDirective) {
33256                   activeDirective.$$addStateInfo(ref.state, params);
33257                 }
33258                 if (newHref === null) {
33259                   nav = false;
33260                   return false;
33261                 }
33262                 attrs.$set(attr, newHref);
33263               };
33264
33265               if (ref.paramExpr) {
33266                 scope.$watch(ref.paramExpr, function(newVal, oldVal) {
33267                   if (newVal !== params) update(newVal);
33268                 }, true);
33269                 params = angular.copy(scope.$eval(ref.paramExpr));
33270               }
33271               update();
33272
33273               if (isForm) return;
33274
33275               element.bind("click", function(e) {
33276                 var button = e.which || e.button;
33277                 if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
33278                   // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33279                   var transition = $timeout(function() {
33280                     $state.go(ref.state, params, options);
33281                   });
33282                   e.preventDefault();
33283
33284                   // if the state has no URL, ignore one preventDefault from the <a> directive.
33285                   var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
33286                   e.preventDefault = function() {
33287                     if (ignorePreventDefaultCount-- <= 0)
33288                       $timeout.cancel(transition);
33289                   };
33290                 }
33291               });
33292             }
33293           };
33294         }
33295
33296         /**
33297          * @ngdoc directive
33298          * @name ui.router.state.directive:ui-sref-active
33299          *
33300          * @requires ui.router.state.$state
33301          * @requires ui.router.state.$stateParams
33302          * @requires $interpolate
33303          *
33304          * @restrict A
33305          *
33306          * @description
33307          * A directive working alongside ui-sref to add classes to an element when the
33308          * related ui-sref directive's state is active, and removing them when it is inactive.
33309          * The primary use-case is to simplify the special appearance of navigation menus
33310          * relying on `ui-sref`, by having the "active" state's menu button appear different,
33311          * distinguishing it from the inactive menu items.
33312          *
33313          * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
33314          * ui-sref-active found at the same level or above the ui-sref will be used.
33315          *
33316          * Will activate when the ui-sref's target state or any child state is active. If you
33317          * need to activate only when the ui-sref target state is active and *not* any of
33318          * it's children, then you will use
33319          * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
33320          *
33321          * @example
33322          * Given the following template:
33323          * <pre>
33324          * <ul>
33325          *   <li ui-sref-active="active" class="item">
33326          *     <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
33327          *   </li>
33328          * </ul>
33329          * </pre>
33330          *
33331          *
33332          * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
33333          * the resulting HTML will appear as (note the 'active' class):
33334          * <pre>
33335          * <ul>
33336          *   <li ui-sref-active="active" class="item active">
33337          *     <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
33338          *   </li>
33339          * </ul>
33340          * </pre>
33341          *
33342          * The class name is interpolated **once** during the directives link time (any further changes to the
33343          * interpolated value are ignored).
33344          *
33345          * Multiple classes may be specified in a space-separated format:
33346          * <pre>
33347          * <ul>
33348          *   <li ui-sref-active='class1 class2 class3'>
33349          *     <a ui-sref="app.user">link</a>
33350          *   </li>
33351          * </ul>
33352          * </pre>
33353          */
33354
33355         /**
33356          * @ngdoc directive
33357          * @name ui.router.state.directive:ui-sref-active-eq
33358          *
33359          * @requires ui.router.state.$state
33360          * @requires ui.router.state.$stateParams
33361          * @requires $interpolate
33362          *
33363          * @restrict A
33364          *
33365          * @description
33366          * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
33367          * when the exact target state used in the `ui-sref` is active; no child states.
33368          *
33369          */
33370         $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
33371         function $StateRefActiveDirective($state, $stateParams, $interpolate) {
33372           return  {
33373             restrict: "A",
33374             controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
33375               var states = [], activeClass;
33376
33377               // There probably isn't much point in $observing this
33378               // uiSrefActive and uiSrefActiveEq share the same directive object with some
33379               // slight difference in logic routing
33380               activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
33381
33382               // Allow uiSref to communicate with uiSrefActive[Equals]
33383               this.$$addStateInfo = function (newState, newParams) {
33384                 var state = $state.get(newState, stateContext($element));
33385
33386                 states.push({
33387                   state: state || { name: newState },
33388                   params: newParams
33389                 });
33390
33391                 update();
33392               };
33393
33394               $scope.$on('$stateChangeSuccess', update);
33395
33396               // Update route state
33397               function update() {
33398                 if (anyMatch()) {
33399                   $element.addClass(activeClass);
33400                 } else {
33401                   $element.removeClass(activeClass);
33402                 }
33403               }
33404
33405               function anyMatch() {
33406                 for (var i = 0; i < states.length; i++) {
33407                   if (isMatch(states[i].state, states[i].params)) {
33408                     return true;
33409                   }
33410                 }
33411                 return false;
33412               }
33413
33414               function isMatch(state, params) {
33415                 if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
33416                   return $state.is(state.name, params);
33417                 } else {
33418                   return $state.includes(state.name, params);
33419                 }
33420               }
33421             }]
33422           };
33423         }
33424
33425         angular.module('ui.router.state')
33426           .directive('uiSref', $StateRefDirective)
33427           .directive('uiSrefActive', $StateRefActiveDirective)
33428           .directive('uiSrefActiveEq', $StateRefActiveDirective);
33429
33430         /**
33431          * @ngdoc filter
33432          * @name ui.router.state.filter:isState
33433          *
33434          * @requires ui.router.state.$state
33435          *
33436          * @description
33437          * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
33438          */
33439         $IsStateFilter.$inject = ['$state'];
33440         function $IsStateFilter($state) {
33441           var isFilter = function (state) {
33442             return $state.is(state);
33443           };
33444           isFilter.$stateful = true;
33445           return isFilter;
33446         }
33447
33448         /**
33449          * @ngdoc filter
33450          * @name ui.router.state.filter:includedByState
33451          *
33452          * @requires ui.router.state.$state
33453          *
33454          * @description
33455          * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
33456          */
33457         $IncludedByStateFilter.$inject = ['$state'];
33458         function $IncludedByStateFilter($state) {
33459           var includesFilter = function (state) {
33460             return $state.includes(state);
33461           };
33462           includesFilter.$stateful = true;
33463           return  includesFilter;
33464         }
33465
33466         angular.module('ui.router.state')
33467           .filter('isState', $IsStateFilter)
33468           .filter('includedByState', $IncludedByStateFilter);
33469         })(window, window.angular);
33470
33471 /***/ },
33472 /* 4 */
33473 /***/ function(module, exports, __webpack_require__) {
33474
33475         __webpack_require__(5);
33476         module.exports = 'ngResource';
33477
33478
33479 /***/ },
33480 /* 5 */
33481 /***/ function(module, exports) {
33482
33483         /**
33484          * @license AngularJS v1.4.8
33485          * (c) 2010-2015 Google, Inc. http://angularjs.org
33486          * License: MIT
33487          */
33488         (function(window, angular, undefined) {'use strict';
33489
33490         var $resourceMinErr = angular.$$minErr('$resource');
33491
33492         // Helper functions and regex to lookup a dotted path on an object
33493         // stopping at undefined/null.  The path must be composed of ASCII
33494         // identifiers (just like $parse)
33495         var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
33496
33497         function isValidDottedPath(path) {
33498           return (path != null && path !== '' && path !== 'hasOwnProperty' &&
33499               MEMBER_NAME_REGEX.test('.' + path));
33500         }
33501
33502         function lookupDottedPath(obj, path) {
33503           if (!isValidDottedPath(path)) {
33504             throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
33505           }
33506           var keys = path.split('.');
33507           for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
33508             var key = keys[i];
33509             obj = (obj !== null) ? obj[key] : undefined;
33510           }
33511           return obj;
33512         }
33513
33514         /**
33515          * Create a shallow copy of an object and clear other fields from the destination
33516          */
33517         function shallowClearAndCopy(src, dst) {
33518           dst = dst || {};
33519
33520           angular.forEach(dst, function(value, key) {
33521             delete dst[key];
33522           });
33523
33524           for (var key in src) {
33525             if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
33526               dst[key] = src[key];
33527             }
33528           }
33529
33530           return dst;
33531         }
33532
33533         /**
33534          * @ngdoc module
33535          * @name ngResource
33536          * @description
33537          *
33538          * # ngResource
33539          *
33540          * The `ngResource` module provides interaction support with RESTful services
33541          * via the $resource service.
33542          *
33543          *
33544          * <div doc-module-components="ngResource"></div>
33545          *
33546          * See {@link ngResource.$resource `$resource`} for usage.
33547          */
33548
33549         /**
33550          * @ngdoc service
33551          * @name $resource
33552          * @requires $http
33553          *
33554          * @description
33555          * A factory which creates a resource object that lets you interact with
33556          * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
33557          *
33558          * The returned resource object has action methods which provide high-level behaviors without
33559          * the need to interact with the low level {@link ng.$http $http} service.
33560          *
33561          * Requires the {@link ngResource `ngResource`} module to be installed.
33562          *
33563          * By default, trailing slashes will be stripped from the calculated URLs,
33564          * which can pose problems with server backends that do not expect that
33565          * behavior.  This can be disabled by configuring the `$resourceProvider` like
33566          * this:
33567          *
33568          * ```js
33569              app.config(['$resourceProvider', function($resourceProvider) {
33570                // Don't strip trailing slashes from calculated URLs
33571                $resourceProvider.defaults.stripTrailingSlashes = false;
33572              }]);
33573          * ```
33574          *
33575          * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
33576          *   `/user/:username`. If you are using a URL with a port number (e.g.
33577          *   `http://example.com:8080/api`), it will be respected.
33578          *
33579          *   If you are using a url with a suffix, just add the suffix, like this:
33580          *   `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
33581          *   or even `$resource('http://example.com/resource/:resource_id.:format')`
33582          *   If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
33583          *   collapsed down to a single `.`.  If you need this sequence to appear and not collapse then you
33584          *   can escape it with `/\.`.
33585          *
33586          * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
33587          *   `actions` methods. If any of the parameter value is a function, it will be executed every time
33588          *   when a param value needs to be obtained for a request (unless the param was overridden).
33589          *
33590          *   Each key value in the parameter object is first bound to url template if present and then any
33591          *   excess keys are appended to the url search query after the `?`.
33592          *
33593          *   Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
33594          *   URL `/path/greet?salutation=Hello`.
33595          *
33596          *   If the parameter value is prefixed with `@` then the value for that parameter will be extracted
33597          *   from the corresponding property on the `data` object (provided when calling an action method).  For
33598          *   example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
33599          *   will be `data.someProp`.
33600          *
33601          * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
33602          *   the default set of resource actions. The declaration should be created in the format of {@link
33603          *   ng.$http#usage $http.config}:
33604          *
33605          *       {action1: {method:?, params:?, isArray:?, headers:?, ...},
33606          *        action2: {method:?, params:?, isArray:?, headers:?, ...},
33607          *        ...}
33608          *
33609          *   Where:
33610          *
33611          *   - **`action`** – {string} – The name of action. This name becomes the name of the method on
33612          *     your resource object.
33613          *   - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
33614          *     `DELETE`, `JSONP`, etc).
33615          *   - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
33616          *     the parameter value is a function, it will be executed every time when a param value needs to
33617          *     be obtained for a request (unless the param was overridden).
33618          *   - **`url`** – {string} – action specific `url` override. The url templating is supported just
33619          *     like for the resource-level urls.
33620          *   - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
33621          *     see `returns` section.
33622          *   - **`transformRequest`** –
33623          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33624          *     transform function or an array of such functions. The transform function takes the http
33625          *     request body and headers and returns its transformed (typically serialized) version.
33626          *     By default, transformRequest will contain one function that checks if the request data is
33627          *     an object and serializes to using `angular.toJson`. To prevent this behavior, set
33628          *     `transformRequest` to an empty array: `transformRequest: []`
33629          *   - **`transformResponse`** –
33630          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33631          *     transform function or an array of such functions. The transform function takes the http
33632          *     response body and headers and returns its transformed (typically deserialized) version.
33633          *     By default, transformResponse will contain one function that checks if the response looks like
33634          *     a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
33635          *     `transformResponse` to an empty array: `transformResponse: []`
33636          *   - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
33637          *     GET request, otherwise if a cache instance built with
33638          *     {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
33639          *     caching.
33640          *   - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
33641          *     should abort the request when resolved.
33642          *   - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
33643          *     XHR object. See
33644          *     [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
33645          *     for more information.
33646          *   - **`responseType`** - `{string}` - see
33647          *     [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
33648          *   - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
33649          *     `response` and `responseError`. Both `response` and `responseError` interceptors get called
33650          *     with `http response` object. See {@link ng.$http $http interceptors}.
33651          *
33652          * @param {Object} options Hash with custom settings that should extend the
33653          *   default `$resourceProvider` behavior.  The only supported option is
33654          *
33655          *   Where:
33656          *
33657          *   - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
33658          *   slashes from any calculated URL will be stripped. (Defaults to true.)
33659          *
33660          * @returns {Object} A resource "class" object with methods for the default set of resource actions
33661          *   optionally extended with custom `actions`. The default set contains these actions:
33662          *   ```js
33663          *   { 'get':    {method:'GET'},
33664          *     'save':   {method:'POST'},
33665          *     'query':  {method:'GET', isArray:true},
33666          *     'remove': {method:'DELETE'},
33667          *     'delete': {method:'DELETE'} };
33668          *   ```
33669          *
33670          *   Calling these methods invoke an {@link ng.$http} with the specified http method,
33671          *   destination and parameters. When the data is returned from the server then the object is an
33672          *   instance of the resource class. The actions `save`, `remove` and `delete` are available on it
33673          *   as  methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
33674          *   read, update, delete) on server-side data like this:
33675          *   ```js
33676          *   var User = $resource('/user/:userId', {userId:'@id'});
33677          *   var user = User.get({userId:123}, function() {
33678          *     user.abc = true;
33679          *     user.$save();
33680          *   });
33681          *   ```
33682          *
33683          *   It is important to realize that invoking a $resource object method immediately returns an
33684          *   empty reference (object or array depending on `isArray`). Once the data is returned from the
33685          *   server the existing reference is populated with the actual data. This is a useful trick since
33686          *   usually the resource is assigned to a model which is then rendered by the view. Having an empty
33687          *   object results in no rendering, once the data arrives from the server then the object is
33688          *   populated with the data and the view automatically re-renders itself showing the new data. This
33689          *   means that in most cases one never has to write a callback function for the action methods.
33690          *
33691          *   The action methods on the class object or instance object can be invoked with the following
33692          *   parameters:
33693          *
33694          *   - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
33695          *   - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
33696          *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
33697          *
33698          *
33699          *   Success callback is called with (value, responseHeaders) arguments, where the value is
33700          *   the populated resource instance or collection object. The error callback is called
33701          *   with (httpResponse) argument.
33702          *
33703          *   Class actions return empty instance (with additional properties below).
33704          *   Instance actions return promise of the action.
33705          *
33706          *   The Resource instances and collection have these additional properties:
33707          *
33708          *   - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
33709          *     instance or collection.
33710          *
33711          *     On success, the promise is resolved with the same resource instance or collection object,
33712          *     updated with data from server. This makes it easy to use in
33713          *     {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
33714          *     rendering until the resource(s) are loaded.
33715          *
33716          *     On failure, the promise is resolved with the {@link ng.$http http response} object, without
33717          *     the `resource` property.
33718          *
33719          *     If an interceptor object was provided, the promise will instead be resolved with the value
33720          *     returned by the interceptor.
33721          *
33722          *   - `$resolved`: `true` after first server interaction is completed (either with success or
33723          *      rejection), `false` before that. Knowing if the Resource has been resolved is useful in
33724          *      data-binding.
33725          *
33726          * @example
33727          *
33728          * # Credit card resource
33729          *
33730          * ```js
33731              // Define CreditCard class
33732              var CreditCard = $resource('/user/:userId/card/:cardId',
33733               {userId:123, cardId:'@id'}, {
33734                charge: {method:'POST', params:{charge:true}}
33735               });
33736
33737              // We can retrieve a collection from the server
33738              var cards = CreditCard.query(function() {
33739                // GET: /user/123/card
33740                // server returns: [ {id:456, number:'1234', name:'Smith'} ];
33741
33742                var card = cards[0];
33743                // each item is an instance of CreditCard
33744                expect(card instanceof CreditCard).toEqual(true);
33745                card.name = "J. Smith";
33746                // non GET methods are mapped onto the instances
33747                card.$save();
33748                // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
33749                // server returns: {id:456, number:'1234', name: 'J. Smith'};
33750
33751                // our custom method is mapped as well.
33752                card.$charge({amount:9.99});
33753                // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
33754              });
33755
33756              // we can create an instance as well
33757              var newCard = new CreditCard({number:'0123'});
33758              newCard.name = "Mike Smith";
33759              newCard.$save();
33760              // POST: /user/123/card {number:'0123', name:'Mike Smith'}
33761              // server returns: {id:789, number:'0123', name: 'Mike Smith'};
33762              expect(newCard.id).toEqual(789);
33763          * ```
33764          *
33765          * The object returned from this function execution is a resource "class" which has "static" method
33766          * for each action in the definition.
33767          *
33768          * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
33769          * `headers`.
33770          * When the data is returned from the server then the object is an instance of the resource type and
33771          * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
33772          * operations (create, read, update, delete) on server-side data.
33773
33774            ```js
33775              var User = $resource('/user/:userId', {userId:'@id'});
33776              User.get({userId:123}, function(user) {
33777                user.abc = true;
33778                user.$save();
33779              });
33780            ```
33781          *
33782          * It's worth noting that the success callback for `get`, `query` and other methods gets passed
33783          * in the response that came from the server as well as $http header getter function, so one
33784          * could rewrite the above example and get access to http headers as:
33785          *
33786            ```js
33787              var User = $resource('/user/:userId', {userId:'@id'});
33788              User.get({userId:123}, function(u, getResponseHeaders){
33789                u.abc = true;
33790                u.$save(function(u, putResponseHeaders) {
33791                  //u => saved user object
33792                  //putResponseHeaders => $http header getter
33793                });
33794              });
33795            ```
33796          *
33797          * You can also access the raw `$http` promise via the `$promise` property on the object returned
33798          *
33799            ```
33800              var User = $resource('/user/:userId', {userId:'@id'});
33801              User.get({userId:123})
33802                  .$promise.then(function(user) {
33803                    $scope.user = user;
33804                  });
33805            ```
33806
33807          * # Creating a custom 'PUT' request
33808          * In this example we create a custom method on our resource to make a PUT request
33809          * ```js
33810          *    var app = angular.module('app', ['ngResource', 'ngRoute']);
33811          *
33812          *    // Some APIs expect a PUT request in the format URL/object/ID
33813          *    // Here we are creating an 'update' method
33814          *    app.factory('Notes', ['$resource', function($resource) {
33815          *    return $resource('/notes/:id', null,
33816          *        {
33817          *            'update': { method:'PUT' }
33818          *        });
33819          *    }]);
33820          *
33821          *    // In our controller we get the ID from the URL using ngRoute and $routeParams
33822          *    // We pass in $routeParams and our Notes factory along with $scope
33823          *    app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
33824                                               function($scope, $routeParams, Notes) {
33825          *    // First get a note object from the factory
33826          *    var note = Notes.get({ id:$routeParams.id });
33827          *    $id = note.id;
33828          *
33829          *    // Now call update passing in the ID first then the object you are updating
33830          *    Notes.update({ id:$id }, note);
33831          *
33832          *    // This will PUT /notes/ID with the note object in the request payload
33833          *    }]);
33834          * ```
33835          */
33836         angular.module('ngResource', ['ng']).
33837           provider('$resource', function() {
33838             var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
33839             var provider = this;
33840
33841             this.defaults = {
33842               // Strip slashes by default
33843               stripTrailingSlashes: true,
33844
33845               // Default actions configuration
33846               actions: {
33847                 'get': {method: 'GET'},
33848                 'save': {method: 'POST'},
33849                 'query': {method: 'GET', isArray: true},
33850                 'remove': {method: 'DELETE'},
33851                 'delete': {method: 'DELETE'}
33852               }
33853             };
33854
33855             this.$get = ['$http', '$q', function($http, $q) {
33856
33857               var noop = angular.noop,
33858                 forEach = angular.forEach,
33859                 extend = angular.extend,
33860                 copy = angular.copy,
33861                 isFunction = angular.isFunction;
33862
33863               /**
33864                * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
33865                * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
33866                * (pchar) allowed in path segments:
33867                *    segment       = *pchar
33868                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33869                *    pct-encoded   = "%" HEXDIG HEXDIG
33870                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33871                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33872                *                     / "*" / "+" / "," / ";" / "="
33873                */
33874               function encodeUriSegment(val) {
33875                 return encodeUriQuery(val, true).
33876                   replace(/%26/gi, '&').
33877                   replace(/%3D/gi, '=').
33878                   replace(/%2B/gi, '+');
33879               }
33880
33881
33882               /**
33883                * This method is intended for encoding *key* or *value* parts of query component. We need a
33884                * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
33885                * have to be encoded per http://tools.ietf.org/html/rfc3986:
33886                *    query       = *( pchar / "/" / "?" )
33887                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33888                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33889                *    pct-encoded   = "%" HEXDIG HEXDIG
33890                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33891                *                     / "*" / "+" / "," / ";" / "="
33892                */
33893               function encodeUriQuery(val, pctEncodeSpaces) {
33894                 return encodeURIComponent(val).
33895                   replace(/%40/gi, '@').
33896                   replace(/%3A/gi, ':').
33897                   replace(/%24/g, '$').
33898                   replace(/%2C/gi, ',').
33899                   replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
33900               }
33901
33902               function Route(template, defaults) {
33903                 this.template = template;
33904                 this.defaults = extend({}, provider.defaults, defaults);
33905                 this.urlParams = {};
33906               }
33907
33908               Route.prototype = {
33909                 setUrlParams: function(config, params, actionUrl) {
33910                   var self = this,
33911                     url = actionUrl || self.template,
33912                     val,
33913                     encodedVal,
33914                     protocolAndDomain = '';
33915
33916                   var urlParams = self.urlParams = {};
33917                   forEach(url.split(/\W/), function(param) {
33918                     if (param === 'hasOwnProperty') {
33919                       throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
33920                     }
33921                     if (!(new RegExp("^\\d+$").test(param)) && param &&
33922                       (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
33923                       urlParams[param] = true;
33924                     }
33925                   });
33926                   url = url.replace(/\\:/g, ':');
33927                   url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
33928                     protocolAndDomain = match;
33929                     return '';
33930                   });
33931
33932                   params = params || {};
33933                   forEach(self.urlParams, function(_, urlParam) {
33934                     val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
33935                     if (angular.isDefined(val) && val !== null) {
33936                       encodedVal = encodeUriSegment(val);
33937                       url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
33938                         return encodedVal + p1;
33939                       });
33940                     } else {
33941                       url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
33942                           leadingSlashes, tail) {
33943                         if (tail.charAt(0) == '/') {
33944                           return tail;
33945                         } else {
33946                           return leadingSlashes + tail;
33947                         }
33948                       });
33949                     }
33950                   });
33951
33952                   // strip trailing slashes and set the url (unless this behavior is specifically disabled)
33953                   if (self.defaults.stripTrailingSlashes) {
33954                     url = url.replace(/\/+$/, '') || '/';
33955                   }
33956
33957                   // then replace collapse `/.` if found in the last URL path segment before the query
33958                   // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
33959                   url = url.replace(/\/\.(?=\w+($|\?))/, '.');
33960                   // replace escaped `/\.` with `/.`
33961                   config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
33962
33963
33964                   // set params - delegate param encoding to $http
33965                   forEach(params, function(value, key) {
33966                     if (!self.urlParams[key]) {
33967                       config.params = config.params || {};
33968                       config.params[key] = value;
33969                     }
33970                   });
33971                 }
33972               };
33973
33974
33975               function resourceFactory(url, paramDefaults, actions, options) {
33976                 var route = new Route(url, options);
33977
33978                 actions = extend({}, provider.defaults.actions, actions);
33979
33980                 function extractParams(data, actionParams) {
33981                   var ids = {};
33982                   actionParams = extend({}, paramDefaults, actionParams);
33983                   forEach(actionParams, function(value, key) {
33984                     if (isFunction(value)) { value = value(); }
33985                     ids[key] = value && value.charAt && value.charAt(0) == '@' ?
33986                       lookupDottedPath(data, value.substr(1)) : value;
33987                   });
33988                   return ids;
33989                 }
33990
33991                 function defaultResponseInterceptor(response) {
33992                   return response.resource;
33993                 }
33994
33995                 function Resource(value) {
33996                   shallowClearAndCopy(value || {}, this);
33997                 }
33998
33999                 Resource.prototype.toJSON = function() {
34000                   var data = extend({}, this);
34001                   delete data.$promise;
34002                   delete data.$resolved;
34003                   return data;
34004                 };
34005
34006                 forEach(actions, function(action, name) {
34007                   var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
34008
34009                   Resource[name] = function(a1, a2, a3, a4) {
34010                     var params = {}, data, success, error;
34011
34012                     /* jshint -W086 */ /* (purposefully fall through case statements) */
34013                     switch (arguments.length) {
34014                       case 4:
34015                         error = a4;
34016                         success = a3;
34017                       //fallthrough
34018                       case 3:
34019                       case 2:
34020                         if (isFunction(a2)) {
34021                           if (isFunction(a1)) {
34022                             success = a1;
34023                             error = a2;
34024                             break;
34025                           }
34026
34027                           success = a2;
34028                           error = a3;
34029                           //fallthrough
34030                         } else {
34031                           params = a1;
34032                           data = a2;
34033                           success = a3;
34034                           break;
34035                         }
34036                       case 1:
34037                         if (isFunction(a1)) success = a1;
34038                         else if (hasBody) data = a1;
34039                         else params = a1;
34040                         break;
34041                       case 0: break;
34042                       default:
34043                         throw $resourceMinErr('badargs',
34044                           "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
34045                           arguments.length);
34046                     }
34047                     /* jshint +W086 */ /* (purposefully fall through case statements) */
34048
34049                     var isInstanceCall = this instanceof Resource;
34050                     var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
34051                     var httpConfig = {};
34052                     var responseInterceptor = action.interceptor && action.interceptor.response ||
34053                       defaultResponseInterceptor;
34054                     var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
34055                       undefined;
34056
34057                     forEach(action, function(value, key) {
34058                       switch (key) {
34059                         default:
34060                           httpConfig[key] = copy(value);
34061                           break;
34062                         case 'params':
34063                         case 'isArray':
34064                         case 'interceptor':
34065                           break;
34066                         case 'timeout':
34067                           httpConfig[key] = value;
34068                           break;
34069                       }
34070                     });
34071
34072                     if (hasBody) httpConfig.data = data;
34073                     route.setUrlParams(httpConfig,
34074                       extend({}, extractParams(data, action.params || {}), params),
34075                       action.url);
34076
34077                     var promise = $http(httpConfig).then(function(response) {
34078                       var data = response.data,
34079                         promise = value.$promise;
34080
34081                       if (data) {
34082                         // Need to convert action.isArray to boolean in case it is undefined
34083                         // jshint -W018
34084                         if (angular.isArray(data) !== (!!action.isArray)) {
34085                           throw $resourceMinErr('badcfg',
34086                               'Error in resource configuration for action `{0}`. Expected response to ' +
34087                               'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
34088                             angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
34089                         }
34090                         // jshint +W018
34091                         if (action.isArray) {
34092                           value.length = 0;
34093                           forEach(data, function(item) {
34094                             if (typeof item === "object") {
34095                               value.push(new Resource(item));
34096                             } else {
34097                               // Valid JSON values may be string literals, and these should not be converted
34098                               // into objects. These items will not have access to the Resource prototype
34099                               // methods, but unfortunately there
34100                               value.push(item);
34101                             }
34102                           });
34103                         } else {
34104                           shallowClearAndCopy(data, value);
34105                           value.$promise = promise;
34106                         }
34107                       }
34108
34109                       value.$resolved = true;
34110
34111                       response.resource = value;
34112
34113                       return response;
34114                     }, function(response) {
34115                       value.$resolved = true;
34116
34117                       (error || noop)(response);
34118
34119                       return $q.reject(response);
34120                     });
34121
34122                     promise = promise.then(
34123                       function(response) {
34124                         var value = responseInterceptor(response);
34125                         (success || noop)(value, response.headers);
34126                         return value;
34127                       },
34128                       responseErrorInterceptor);
34129
34130                     if (!isInstanceCall) {
34131                       // we are creating instance / collection
34132                       // - set the initial promise
34133                       // - return the instance / collection
34134                       value.$promise = promise;
34135                       value.$resolved = false;
34136
34137                       return value;
34138                     }
34139
34140                     // instance call
34141                     return promise;
34142                   };
34143
34144
34145                   Resource.prototype['$' + name] = function(params, success, error) {
34146                     if (isFunction(params)) {
34147                       error = success; success = params; params = {};
34148                     }
34149                     var result = Resource[name].call(this, params, this, success, error);
34150                     return result.$promise || result;
34151                   };
34152                 });
34153
34154                 Resource.bind = function(additionalParamDefaults) {
34155                   return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
34156                 };
34157
34158                 return Resource;
34159               }
34160
34161               return resourceFactory;
34162             }];
34163           });
34164
34165
34166         })(window, window.angular);
34167
34168
34169 /***/ },
34170 /* 6 */
34171 /***/ function(module, exports, __webpack_require__) {
34172
34173         __webpack_require__(7);
34174
34175         module.exports = 'ui.bootstrap';
34176
34177
34178 /***/ },
34179 /* 7 */
34180 /***/ function(module, exports) {
34181
34182         /*
34183          * angular-ui-bootstrap
34184          * http://angular-ui.github.io/bootstrap/
34185
34186          * Version: 1.0.0 - 2016-01-08
34187          * License: MIT
34188          */
34189         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"]);
34190         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"]);
34191         angular.module('ui.bootstrap.collapse', [])
34192
34193           .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
34194             var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
34195             return {
34196               link: function(scope, element, attrs) {
34197                 if (!scope.$eval(attrs.uibCollapse)) {
34198                   element.addClass('in')
34199                     .addClass('collapse')
34200                     .css({height: 'auto'});
34201                 }
34202
34203                 function expand() {
34204                   element.removeClass('collapse')
34205                     .addClass('collapsing')
34206                     .attr('aria-expanded', true)
34207                     .attr('aria-hidden', false);
34208
34209                   if ($animateCss) {
34210                     $animateCss(element, {
34211                       addClass: 'in',
34212                       easing: 'ease',
34213                       to: { height: element[0].scrollHeight + 'px' }
34214                     }).start()['finally'](expandDone);
34215                   } else {
34216                     $animate.addClass(element, 'in', {
34217                       to: { height: element[0].scrollHeight + 'px' }
34218                     }).then(expandDone);
34219                   }
34220                 }
34221
34222                 function expandDone() {
34223                   element.removeClass('collapsing')
34224                     .addClass('collapse')
34225                     .css({height: 'auto'});
34226                 }
34227
34228                 function collapse() {
34229                   if (!element.hasClass('collapse') && !element.hasClass('in')) {
34230                     return collapseDone();
34231                   }
34232
34233                   element
34234                     // IMPORTANT: The height must be set before adding "collapsing" class.
34235                     // Otherwise, the browser attempts to animate from height 0 (in
34236                     // collapsing class) to the given height here.
34237                     .css({height: element[0].scrollHeight + 'px'})
34238                     // initially all panel collapse have the collapse class, this removal
34239                     // prevents the animation from jumping to collapsed state
34240                     .removeClass('collapse')
34241                     .addClass('collapsing')
34242                     .attr('aria-expanded', false)
34243                     .attr('aria-hidden', true);
34244
34245                   if ($animateCss) {
34246                     $animateCss(element, {
34247                       removeClass: 'in',
34248                       to: {height: '0'}
34249                     }).start()['finally'](collapseDone);
34250                   } else {
34251                     $animate.removeClass(element, 'in', {
34252                       to: {height: '0'}
34253                     }).then(collapseDone);
34254                   }
34255                 }
34256
34257                 function collapseDone() {
34258                   element.css({height: '0'}); // Required so that collapse works when animation is disabled
34259                   element.removeClass('collapsing')
34260                     .addClass('collapse');
34261                 }
34262
34263                 scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
34264                   if (shouldCollapse) {
34265                     collapse();
34266                   } else {
34267                     expand();
34268                   }
34269                 });
34270               }
34271             };
34272           }]);
34273
34274         angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
34275
34276         .constant('uibAccordionConfig', {
34277           closeOthers: true
34278         })
34279
34280         .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
34281           // This array keeps track of the accordion groups
34282           this.groups = [];
34283
34284           // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
34285           this.closeOthers = function(openGroup) {
34286             var closeOthers = angular.isDefined($attrs.closeOthers) ?
34287               $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
34288             if (closeOthers) {
34289               angular.forEach(this.groups, function(group) {
34290                 if (group !== openGroup) {
34291                   group.isOpen = false;
34292                 }
34293               });
34294             }
34295           };
34296
34297           // This is called from the accordion-group directive to add itself to the accordion
34298           this.addGroup = function(groupScope) {
34299             var that = this;
34300             this.groups.push(groupScope);
34301
34302             groupScope.$on('$destroy', function(event) {
34303               that.removeGroup(groupScope);
34304             });
34305           };
34306
34307           // This is called from the accordion-group directive when to remove itself
34308           this.removeGroup = function(group) {
34309             var index = this.groups.indexOf(group);
34310             if (index !== -1) {
34311               this.groups.splice(index, 1);
34312             }
34313           };
34314         }])
34315
34316         // The accordion directive simply sets up the directive controller
34317         // and adds an accordion CSS class to itself element.
34318         .directive('uibAccordion', function() {
34319           return {
34320             controller: 'UibAccordionController',
34321             controllerAs: 'accordion',
34322             transclude: true,
34323             templateUrl: function(element, attrs) {
34324               return attrs.templateUrl || 'uib/template/accordion/accordion.html';
34325             }
34326           };
34327         })
34328
34329         // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
34330         .directive('uibAccordionGroup', function() {
34331           return {
34332             require: '^uibAccordion',         // We need this directive to be inside an accordion
34333             transclude: true,              // It transcludes the contents of the directive into the template
34334             replace: true,                // The element containing the directive will be replaced with the template
34335             templateUrl: function(element, attrs) {
34336               return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
34337             },
34338             scope: {
34339               heading: '@',               // Interpolate the heading attribute onto this scope
34340               isOpen: '=?',
34341               isDisabled: '=?'
34342             },
34343             controller: function() {
34344               this.setHeading = function(element) {
34345                 this.heading = element;
34346               };
34347             },
34348             link: function(scope, element, attrs, accordionCtrl) {
34349               accordionCtrl.addGroup(scope);
34350
34351               scope.openClass = attrs.openClass || 'panel-open';
34352               scope.panelClass = attrs.panelClass || 'panel-default';
34353               scope.$watch('isOpen', function(value) {
34354                 element.toggleClass(scope.openClass, !!value);
34355                 if (value) {
34356                   accordionCtrl.closeOthers(scope);
34357                 }
34358               });
34359
34360               scope.toggleOpen = function($event) {
34361                 if (!scope.isDisabled) {
34362                   if (!$event || $event.which === 32) {
34363                     scope.isOpen = !scope.isOpen;
34364                   }
34365                 }
34366               };
34367             }
34368           };
34369         })
34370
34371         // Use accordion-heading below an accordion-group to provide a heading containing HTML
34372         .directive('uibAccordionHeading', function() {
34373           return {
34374             transclude: true,   // Grab the contents to be used as the heading
34375             template: '',       // In effect remove this element!
34376             replace: true,
34377             require: '^uibAccordionGroup',
34378             link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
34379               // Pass the heading to the accordion-group controller
34380               // so that it can be transcluded into the right place in the template
34381               // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
34382               accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
34383             }
34384           };
34385         })
34386
34387         // Use in the accordion-group template to indicate where you want the heading to be transcluded
34388         // You must provide the property on the accordion-group controller that will hold the transcluded element
34389         .directive('uibAccordionTransclude', function() {
34390           return {
34391             require: '^uibAccordionGroup',
34392             link: function(scope, element, attrs, controller) {
34393               scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
34394                 if (heading) {
34395                   element.find('span').html('');
34396                   element.find('span').append(heading);
34397                 }
34398               });
34399             }
34400           };
34401         });
34402
34403         angular.module('ui.bootstrap.alert', [])
34404
34405         .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
34406           $scope.closeable = !!$attrs.close;
34407
34408           var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
34409             $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
34410
34411           if (dismissOnTimeout) {
34412             $timeout(function() {
34413               $scope.close();
34414             }, parseInt(dismissOnTimeout, 10));
34415           }
34416         }])
34417
34418         .directive('uibAlert', function() {
34419           return {
34420             controller: 'UibAlertController',
34421             controllerAs: 'alert',
34422             templateUrl: function(element, attrs) {
34423               return attrs.templateUrl || 'uib/template/alert/alert.html';
34424             },
34425             transclude: true,
34426             replace: true,
34427             scope: {
34428               type: '@',
34429               close: '&'
34430             }
34431           };
34432         });
34433
34434         angular.module('ui.bootstrap.buttons', [])
34435
34436         .constant('uibButtonConfig', {
34437           activeClass: 'active',
34438           toggleEvent: 'click'
34439         })
34440
34441         .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
34442           this.activeClass = buttonConfig.activeClass || 'active';
34443           this.toggleEvent = buttonConfig.toggleEvent || 'click';
34444         }])
34445
34446         .directive('uibBtnRadio', ['$parse', function($parse) {
34447           return {
34448             require: ['uibBtnRadio', 'ngModel'],
34449             controller: 'UibButtonsController',
34450             controllerAs: 'buttons',
34451             link: function(scope, element, attrs, ctrls) {
34452               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34453               var uncheckableExpr = $parse(attrs.uibUncheckable);
34454
34455               element.find('input').css({display: 'none'});
34456
34457               //model -> UI
34458               ngModelCtrl.$render = function() {
34459                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
34460               };
34461
34462               //ui->model
34463               element.on(buttonsCtrl.toggleEvent, function() {
34464                 if (attrs.disabled) {
34465                   return;
34466                 }
34467
34468                 var isActive = element.hasClass(buttonsCtrl.activeClass);
34469
34470                 if (!isActive || angular.isDefined(attrs.uncheckable)) {
34471                   scope.$apply(function() {
34472                     ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
34473                     ngModelCtrl.$render();
34474                   });
34475                 }
34476               });
34477
34478               if (attrs.uibUncheckable) {
34479                 scope.$watch(uncheckableExpr, function(uncheckable) {
34480                   attrs.$set('uncheckable', uncheckable ? '' : null);
34481                 });
34482               }
34483             }
34484           };
34485         }])
34486
34487         .directive('uibBtnCheckbox', function() {
34488           return {
34489             require: ['uibBtnCheckbox', 'ngModel'],
34490             controller: 'UibButtonsController',
34491             controllerAs: 'button',
34492             link: function(scope, element, attrs, ctrls) {
34493               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34494
34495               element.find('input').css({display: 'none'});
34496
34497               function getTrueValue() {
34498                 return getCheckboxValue(attrs.btnCheckboxTrue, true);
34499               }
34500
34501               function getFalseValue() {
34502                 return getCheckboxValue(attrs.btnCheckboxFalse, false);
34503               }
34504
34505               function getCheckboxValue(attribute, defaultValue) {
34506                 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
34507               }
34508
34509               //model -> UI
34510               ngModelCtrl.$render = function() {
34511                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
34512               };
34513
34514               //ui->model
34515               element.on(buttonsCtrl.toggleEvent, function() {
34516                 if (attrs.disabled) {
34517                   return;
34518                 }
34519
34520                 scope.$apply(function() {
34521                   ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
34522                   ngModelCtrl.$render();
34523                 });
34524               });
34525             }
34526           };
34527         });
34528
34529         angular.module('ui.bootstrap.carousel', [])
34530
34531         .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
34532           var self = this,
34533             slides = self.slides = $scope.slides = [],
34534             SLIDE_DIRECTION = 'uib-slideDirection',
34535             currentIndex = -1,
34536             currentInterval, isPlaying, bufferedTransitions = [];
34537           self.currentSlide = null;
34538
34539           var destroyed = false;
34540
34541           self.addSlide = function(slide, element) {
34542             slide.$element = element;
34543             slides.push(slide);
34544             //if this is the first slide or the slide is set to active, select it
34545             if (slides.length === 1 || slide.active) {
34546               if ($scope.$currentTransition) {
34547                 $scope.$currentTransition = null;
34548               }
34549
34550               self.select(slides[slides.length - 1]);
34551               if (slides.length === 1) {
34552                 $scope.play();
34553               }
34554             } else {
34555               slide.active = false;
34556             }
34557           };
34558
34559           self.getCurrentIndex = function() {
34560             if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
34561               return +self.currentSlide.index;
34562             }
34563             return currentIndex;
34564           };
34565
34566           self.next = $scope.next = function() {
34567             var newIndex = (self.getCurrentIndex() + 1) % slides.length;
34568
34569             if (newIndex === 0 && $scope.noWrap()) {
34570               $scope.pause();
34571               return;
34572             }
34573
34574             return self.select(getSlideByIndex(newIndex), 'next');
34575           };
34576
34577           self.prev = $scope.prev = function() {
34578             var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
34579
34580             if ($scope.noWrap() && newIndex === slides.length - 1) {
34581               $scope.pause();
34582               return;
34583             }
34584
34585             return self.select(getSlideByIndex(newIndex), 'prev');
34586           };
34587
34588           self.removeSlide = function(slide) {
34589             if (angular.isDefined(slide.index)) {
34590               slides.sort(function(a, b) {
34591                 return +a.index > +b.index;
34592               });
34593             }
34594
34595             var bufferedIndex = bufferedTransitions.indexOf(slide);
34596             if (bufferedIndex !== -1) {
34597               bufferedTransitions.splice(bufferedIndex, 1);
34598             }
34599             //get the index of the slide inside the carousel
34600             var index = slides.indexOf(slide);
34601             slides.splice(index, 1);
34602             $timeout(function() {
34603               if (slides.length > 0 && slide.active) {
34604                 if (index >= slides.length) {
34605                   self.select(slides[index - 1]);
34606                 } else {
34607                   self.select(slides[index]);
34608                 }
34609               } else if (currentIndex > index) {
34610                 currentIndex--;
34611               }
34612             });
34613
34614             //clean the currentSlide when no more slide
34615             if (slides.length === 0) {
34616               self.currentSlide = null;
34617               clearBufferedTransitions();
34618             }
34619           };
34620
34621           /* direction: "prev" or "next" */
34622           self.select = $scope.select = function(nextSlide, direction) {
34623             var nextIndex = $scope.indexOfSlide(nextSlide);
34624             //Decide direction if it's not given
34625             if (direction === undefined) {
34626               direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34627             }
34628             //Prevent this user-triggered transition from occurring if there is already one in progress
34629             if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
34630               goNext(nextSlide, nextIndex, direction);
34631             } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
34632               bufferedTransitions.push(nextSlide);
34633             }
34634           };
34635
34636           /* Allow outside people to call indexOf on slides array */
34637           $scope.indexOfSlide = function(slide) {
34638             return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
34639           };
34640
34641           $scope.isActive = function(slide) {
34642             return self.currentSlide === slide;
34643           };
34644
34645           $scope.pause = function() {
34646             if (!$scope.noPause) {
34647               isPlaying = false;
34648               resetTimer();
34649             }
34650           };
34651
34652           $scope.play = function() {
34653             if (!isPlaying) {
34654               isPlaying = true;
34655               restartTimer();
34656             }
34657           };
34658
34659           $scope.$on('$destroy', function() {
34660             destroyed = true;
34661             resetTimer();
34662           });
34663
34664           $scope.$watch('noTransition', function(noTransition) {
34665             $animate.enabled($element, !noTransition);
34666           });
34667
34668           $scope.$watch('interval', restartTimer);
34669
34670           $scope.$watchCollection('slides', resetTransition);
34671
34672           function clearBufferedTransitions() {
34673             while (bufferedTransitions.length) {
34674               bufferedTransitions.shift();
34675             }
34676           }
34677
34678           function getSlideByIndex(index) {
34679             if (angular.isUndefined(slides[index].index)) {
34680               return slides[index];
34681             }
34682             for (var i = 0, l = slides.length; i < l; ++i) {
34683               if (slides[i].index === index) {
34684                 return slides[i];
34685               }
34686             }
34687           }
34688
34689           function goNext(slide, index, direction) {
34690             if (destroyed) { return; }
34691
34692             angular.extend(slide, {direction: direction, active: true});
34693             angular.extend(self.currentSlide || {}, {direction: direction, active: false});
34694             if ($animate.enabled($element) && !$scope.$currentTransition &&
34695               slide.$element && self.slides.length > 1) {
34696               slide.$element.data(SLIDE_DIRECTION, slide.direction);
34697               if (self.currentSlide && self.currentSlide.$element) {
34698                 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
34699               }
34700
34701               $scope.$currentTransition = true;
34702               $animate.on('addClass', slide.$element, function(element, phase) {
34703                 if (phase === 'close') {
34704                   $scope.$currentTransition = null;
34705                   $animate.off('addClass', element);
34706                   if (bufferedTransitions.length) {
34707                     var nextSlide = bufferedTransitions.pop();
34708                     var nextIndex = $scope.indexOfSlide(nextSlide);
34709                     var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34710                     clearBufferedTransitions();
34711
34712                     goNext(nextSlide, nextIndex, nextDirection);
34713                   }
34714                 }
34715               });
34716             }
34717
34718             self.currentSlide = slide;
34719             currentIndex = index;
34720
34721             //every time you change slides, reset the timer
34722             restartTimer();
34723           }
34724
34725           function resetTimer() {
34726             if (currentInterval) {
34727               $interval.cancel(currentInterval);
34728               currentInterval = null;
34729             }
34730           }
34731
34732           function resetTransition(slides) {
34733             if (!slides.length) {
34734               $scope.$currentTransition = null;
34735               clearBufferedTransitions();
34736             }
34737           }
34738
34739           function restartTimer() {
34740             resetTimer();
34741             var interval = +$scope.interval;
34742             if (!isNaN(interval) && interval > 0) {
34743               currentInterval = $interval(timerFn, interval);
34744             }
34745           }
34746
34747           function timerFn() {
34748             var interval = +$scope.interval;
34749             if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
34750               $scope.next();
34751             } else {
34752               $scope.pause();
34753             }
34754           }
34755         }])
34756
34757         .directive('uibCarousel', function() {
34758           return {
34759             transclude: true,
34760             replace: true,
34761             controller: 'UibCarouselController',
34762             controllerAs: 'carousel',
34763             templateUrl: function(element, attrs) {
34764               return attrs.templateUrl || 'uib/template/carousel/carousel.html';
34765             },
34766             scope: {
34767               interval: '=',
34768               noTransition: '=',
34769               noPause: '=',
34770               noWrap: '&'
34771             }
34772           };
34773         })
34774
34775         .directive('uibSlide', function() {
34776           return {
34777             require: '^uibCarousel',
34778             transclude: true,
34779             replace: true,
34780             templateUrl: function(element, attrs) {
34781               return attrs.templateUrl || 'uib/template/carousel/slide.html';
34782             },
34783             scope: {
34784               active: '=?',
34785               actual: '=?',
34786               index: '=?'
34787             },
34788             link: function (scope, element, attrs, carouselCtrl) {
34789               carouselCtrl.addSlide(scope, element);
34790               //when the scope is destroyed then remove the slide from the current slides array
34791               scope.$on('$destroy', function() {
34792                 carouselCtrl.removeSlide(scope);
34793               });
34794
34795               scope.$watch('active', function(active) {
34796                 if (active) {
34797                   carouselCtrl.select(scope);
34798                 }
34799               });
34800             }
34801           };
34802         })
34803
34804         .animation('.item', ['$animateCss',
34805         function($animateCss) {
34806           var SLIDE_DIRECTION = 'uib-slideDirection';
34807
34808           function removeClass(element, className, callback) {
34809             element.removeClass(className);
34810             if (callback) {
34811               callback();
34812             }
34813           }
34814
34815           return {
34816             beforeAddClass: function(element, className, done) {
34817               if (className === 'active') {
34818                 var stopped = false;
34819                 var direction = element.data(SLIDE_DIRECTION);
34820                 var directionClass = direction === 'next' ? 'left' : 'right';
34821                 var removeClassFn = removeClass.bind(this, element,
34822                   directionClass + ' ' + direction, done);
34823                 element.addClass(direction);
34824
34825                 $animateCss(element, {addClass: directionClass})
34826                   .start()
34827                   .done(removeClassFn);
34828
34829                 return function() {
34830                   stopped = true;
34831                 };
34832               }
34833               done();
34834             },
34835             beforeRemoveClass: function (element, className, done) {
34836               if (className === 'active') {
34837                 var stopped = false;
34838                 var direction = element.data(SLIDE_DIRECTION);
34839                 var directionClass = direction === 'next' ? 'left' : 'right';
34840                 var removeClassFn = removeClass.bind(this, element, directionClass, done);
34841
34842                 $animateCss(element, {addClass: directionClass})
34843                   .start()
34844                   .done(removeClassFn);
34845
34846                 return function() {
34847                   stopped = true;
34848                 };
34849               }
34850               done();
34851             }
34852           };
34853         }]);
34854
34855         angular.module('ui.bootstrap.dateparser', [])
34856
34857         .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
34858           // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
34859           var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
34860
34861           var localeId;
34862           var formatCodeToRegex;
34863
34864           this.init = function() {
34865             localeId = $locale.id;
34866
34867             this.parsers = {};
34868
34869             formatCodeToRegex = [
34870               {
34871                 key: 'yyyy',
34872                 regex: '\\d{4}',
34873                 apply: function(value) { this.year = +value; }
34874               },
34875               {
34876                 key: 'yy',
34877                 regex: '\\d{2}',
34878                 apply: function(value) { this.year = +value + 2000; }
34879               },
34880               {
34881                 key: 'y',
34882                 regex: '\\d{1,4}',
34883                 apply: function(value) { this.year = +value; }
34884               },
34885               {
34886                 key: 'M!',
34887                 regex: '0?[1-9]|1[0-2]',
34888                 apply: function(value) { this.month = value - 1; }
34889               },
34890               {
34891                 key: 'MMMM',
34892                 regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
34893                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
34894               },
34895               {
34896                 key: 'MMM',
34897                 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
34898                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
34899               },
34900               {
34901                 key: 'MM',
34902                 regex: '0[1-9]|1[0-2]',
34903                 apply: function(value) { this.month = value - 1; }
34904               },
34905               {
34906                 key: 'M',
34907                 regex: '[1-9]|1[0-2]',
34908                 apply: function(value) { this.month = value - 1; }
34909               },
34910               {
34911                 key: 'd!',
34912                 regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
34913                 apply: function(value) { this.date = +value; }
34914               },
34915               {
34916                 key: 'dd',
34917                 regex: '[0-2][0-9]{1}|3[0-1]{1}',
34918                 apply: function(value) { this.date = +value; }
34919               },
34920               {
34921                 key: 'd',
34922                 regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
34923                 apply: function(value) { this.date = +value; }
34924               },
34925               {
34926                 key: 'EEEE',
34927                 regex: $locale.DATETIME_FORMATS.DAY.join('|')
34928               },
34929               {
34930                 key: 'EEE',
34931                 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
34932               },
34933               {
34934                 key: 'HH',
34935                 regex: '(?:0|1)[0-9]|2[0-3]',
34936                 apply: function(value) { this.hours = +value; }
34937               },
34938               {
34939                 key: 'hh',
34940                 regex: '0[0-9]|1[0-2]',
34941                 apply: function(value) { this.hours = +value; }
34942               },
34943               {
34944                 key: 'H',
34945                 regex: '1?[0-9]|2[0-3]',
34946                 apply: function(value) { this.hours = +value; }
34947               },
34948               {
34949                 key: 'h',
34950                 regex: '[0-9]|1[0-2]',
34951                 apply: function(value) { this.hours = +value; }
34952               },
34953               {
34954                 key: 'mm',
34955                 regex: '[0-5][0-9]',
34956                 apply: function(value) { this.minutes = +value; }
34957               },
34958               {
34959                 key: 'm',
34960                 regex: '[0-9]|[1-5][0-9]',
34961                 apply: function(value) { this.minutes = +value; }
34962               },
34963               {
34964                 key: 'sss',
34965                 regex: '[0-9][0-9][0-9]',
34966                 apply: function(value) { this.milliseconds = +value; }
34967               },
34968               {
34969                 key: 'ss',
34970                 regex: '[0-5][0-9]',
34971                 apply: function(value) { this.seconds = +value; }
34972               },
34973               {
34974                 key: 's',
34975                 regex: '[0-9]|[1-5][0-9]',
34976                 apply: function(value) { this.seconds = +value; }
34977               },
34978               {
34979                 key: 'a',
34980                 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
34981                 apply: function(value) {
34982                   if (this.hours === 12) {
34983                     this.hours = 0;
34984                   }
34985
34986                   if (value === 'PM') {
34987                     this.hours += 12;
34988                   }
34989                 }
34990               },
34991               {
34992                 key: 'Z',
34993                 regex: '[+-]\\d{4}',
34994                 apply: function(value) {
34995                   var matches = value.match(/([+-])(\d{2})(\d{2})/),
34996                     sign = matches[1],
34997                     hours = matches[2],
34998                     minutes = matches[3];
34999                   this.hours += toInt(sign + hours);
35000                   this.minutes += toInt(sign + minutes);
35001                 }
35002               },
35003               {
35004                 key: 'ww',
35005                 regex: '[0-4][0-9]|5[0-3]'
35006               },
35007               {
35008                 key: 'w',
35009                 regex: '[0-9]|[1-4][0-9]|5[0-3]'
35010               },
35011               {
35012                 key: 'GGGG',
35013                 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s')
35014               },
35015               {
35016                 key: 'GGG',
35017                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35018               },
35019               {
35020                 key: 'GG',
35021                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35022               },
35023               {
35024                 key: 'G',
35025                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35026               }
35027             ];
35028           };
35029
35030           this.init();
35031
35032           function createParser(format) {
35033             var map = [], regex = format.split('');
35034
35035             // check for literal values
35036             var quoteIndex = format.indexOf('\'');
35037             if (quoteIndex > -1) {
35038               var inLiteral = false;
35039               format = format.split('');
35040               for (var i = quoteIndex; i < format.length; i++) {
35041                 if (inLiteral) {
35042                   if (format[i] === '\'') {
35043                     if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
35044                       format[i+1] = '$';
35045                       regex[i+1] = '';
35046                     } else { // end of literal
35047                       regex[i] = '';
35048                       inLiteral = false;
35049                     }
35050                   }
35051                   format[i] = '$';
35052                 } else {
35053                   if (format[i] === '\'') { // start of literal
35054                     format[i] = '$';
35055                     regex[i] = '';
35056                     inLiteral = true;
35057                   }
35058                 }
35059               }
35060
35061               format = format.join('');
35062             }
35063
35064             angular.forEach(formatCodeToRegex, function(data) {
35065               var index = format.indexOf(data.key);
35066
35067               if (index > -1) {
35068                 format = format.split('');
35069
35070                 regex[index] = '(' + data.regex + ')';
35071                 format[index] = '$'; // Custom symbol to define consumed part of format
35072                 for (var i = index + 1, n = index + data.key.length; i < n; i++) {
35073                   regex[i] = '';
35074                   format[i] = '$';
35075                 }
35076                 format = format.join('');
35077
35078                 map.push({
35079                   index: index,
35080                   apply: data.apply,
35081                   matcher: data.regex
35082                 });
35083               }
35084             });
35085
35086             return {
35087               regex: new RegExp('^' + regex.join('') + '$'),
35088               map: orderByFilter(map, 'index')
35089             };
35090           }
35091
35092           this.parse = function(input, format, baseDate) {
35093             if (!angular.isString(input) || !format) {
35094               return input;
35095             }
35096
35097             format = $locale.DATETIME_FORMATS[format] || format;
35098             format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
35099
35100             if ($locale.id !== localeId) {
35101               this.init();
35102             }
35103
35104             if (!this.parsers[format]) {
35105               this.parsers[format] = createParser(format);
35106             }
35107
35108             var parser = this.parsers[format],
35109                 regex = parser.regex,
35110                 map = parser.map,
35111                 results = input.match(regex),
35112                 tzOffset = false;
35113             if (results && results.length) {
35114               var fields, dt;
35115               if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
35116                 fields = {
35117                   year: baseDate.getFullYear(),
35118                   month: baseDate.getMonth(),
35119                   date: baseDate.getDate(),
35120                   hours: baseDate.getHours(),
35121                   minutes: baseDate.getMinutes(),
35122                   seconds: baseDate.getSeconds(),
35123                   milliseconds: baseDate.getMilliseconds()
35124                 };
35125               } else {
35126                 if (baseDate) {
35127                   $log.warn('dateparser:', 'baseDate is not a valid date');
35128                 }
35129                 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
35130               }
35131
35132               for (var i = 1, n = results.length; i < n; i++) {
35133                 var mapper = map[i - 1];
35134                 if (mapper.matcher === 'Z') {
35135                   tzOffset = true;
35136                 }
35137
35138                 if (mapper.apply) {
35139                   mapper.apply.call(fields, results[i]);
35140                 }
35141               }
35142
35143               var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
35144                 Date.prototype.setFullYear;
35145               var timesetter = tzOffset ? Date.prototype.setUTCHours :
35146                 Date.prototype.setHours;
35147
35148               if (isValid(fields.year, fields.month, fields.date)) {
35149                 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
35150                   dt = new Date(baseDate);
35151                   datesetter.call(dt, fields.year, fields.month, fields.date);
35152                   timesetter.call(dt, fields.hours, fields.minutes,
35153                     fields.seconds, fields.milliseconds);
35154                 } else {
35155                   dt = new Date(0);
35156                   datesetter.call(dt, fields.year, fields.month, fields.date);
35157                   timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
35158                     fields.seconds || 0, fields.milliseconds || 0);
35159                 }
35160               }
35161
35162               return dt;
35163             }
35164           };
35165
35166           // Check if date is valid for specific month (and year for February).
35167           // Month: 0 = Jan, 1 = Feb, etc
35168           function isValid(year, month, date) {
35169             if (date < 1) {
35170               return false;
35171             }
35172
35173             if (month === 1 && date > 28) {
35174               return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
35175             }
35176
35177             if (month === 3 || month === 5 || month === 8 || month === 10) {
35178               return date < 31;
35179             }
35180
35181             return true;
35182           }
35183
35184           function toInt(str) {
35185             return parseInt(str, 10);
35186           }
35187
35188           this.toTimezone = toTimezone;
35189           this.fromTimezone = fromTimezone;
35190           this.timezoneToOffset = timezoneToOffset;
35191           this.addDateMinutes = addDateMinutes;
35192           this.convertTimezoneToLocal = convertTimezoneToLocal;
35193           
35194           function toTimezone(date, timezone) {
35195             return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
35196           }
35197
35198           function fromTimezone(date, timezone) {
35199             return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
35200           }
35201
35202           //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
35203           function timezoneToOffset(timezone, fallback) {
35204             var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
35205             return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
35206           }
35207
35208           function addDateMinutes(date, minutes) {
35209             date = new Date(date.getTime());
35210             date.setMinutes(date.getMinutes() + minutes);
35211             return date;
35212           }
35213
35214           function convertTimezoneToLocal(date, timezone, reverse) {
35215             reverse = reverse ? -1 : 1;
35216             var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
35217             return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
35218           }
35219         }]);
35220
35221         // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
35222         // at most one element.
35223         angular.module('ui.bootstrap.isClass', [])
35224         .directive('uibIsClass', [
35225                  '$animate',
35226         function ($animate) {
35227           //                    11111111          22222222
35228           var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
35229           //                    11111111           22222222
35230           var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
35231
35232           var dataPerTracked = {};
35233
35234           return {
35235             restrict: 'A',
35236             compile: function (tElement, tAttrs) {
35237               var linkedScopes = [];
35238               var instances = [];
35239               var expToData = {};
35240               var lastActivated = null;
35241               var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
35242               var onExp = onExpMatches[2];
35243               var expsStr = onExpMatches[1];
35244               var exps = expsStr.split(',');
35245
35246               return linkFn;
35247
35248               function linkFn(scope, element, attrs) {
35249                 linkedScopes.push(scope);
35250                 instances.push({
35251                   scope: scope,
35252                   element: element
35253                 });
35254
35255                 exps.forEach(function (exp, k) {
35256                   addForExp(exp, scope);
35257                 });
35258
35259                 scope.$on('$destroy', removeScope);
35260               }
35261
35262               function addForExp(exp, scope) {
35263                 var matches = exp.match(IS_REGEXP);
35264                 var clazz = scope.$eval(matches[1]);
35265                 var compareWithExp = matches[2];
35266                 var data = expToData[exp];
35267                 if (!data) {
35268                   var watchFn = function (compareWithVal) {
35269                     var newActivated = null;
35270                     instances.some(function (instance) {
35271                       var thisVal = instance.scope.$eval(onExp);
35272                       if (thisVal === compareWithVal) {
35273                         newActivated = instance;
35274                         return true;
35275                       }
35276                     });
35277                     if (data.lastActivated !== newActivated) {
35278                       if (data.lastActivated) {
35279                         $animate.removeClass(data.lastActivated.element, clazz);
35280                       }
35281                       if (newActivated) {
35282                         $animate.addClass(newActivated.element, clazz);
35283                       }
35284                       data.lastActivated = newActivated;
35285                     }
35286                   };
35287                   expToData[exp] = data = {
35288                     lastActivated: null,
35289                     scope: scope,
35290                     watchFn: watchFn,
35291                     compareWithExp: compareWithExp,
35292                     watcher: scope.$watch(compareWithExp, watchFn)
35293                   };
35294                 }
35295                 data.watchFn(scope.$eval(compareWithExp));
35296               }
35297
35298               function removeScope(e) {
35299                 var removedScope = e.targetScope;
35300                 var index = linkedScopes.indexOf(removedScope);
35301                 linkedScopes.splice(index, 1);
35302                 instances.splice(index, 1);
35303                 if (linkedScopes.length) {
35304                   var newWatchScope = linkedScopes[0];
35305                   angular.forEach(expToData, function (data) {
35306                     if (data.scope === removedScope) {
35307                       data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
35308                       data.scope = newWatchScope;
35309                     }
35310                   });
35311                 }
35312                 else {
35313                   expToData = {};
35314                 }
35315               }
35316             }
35317           };
35318         }]);
35319         angular.module('ui.bootstrap.position', [])
35320
35321         /**
35322          * A set of utility methods for working with the DOM.
35323          * It is meant to be used where we need to absolute-position elements in
35324          * relation to another element (this is the case for tooltips, popovers,
35325          * typeahead suggestions etc.).
35326          */
35327           .factory('$uibPosition', ['$document', '$window', function($document, $window) {
35328             /**
35329              * Used by scrollbarWidth() function to cache scrollbar's width.
35330              * Do not access this variable directly, use scrollbarWidth() instead.
35331              */
35332             var SCROLLBAR_WIDTH;
35333             var OVERFLOW_REGEX = {
35334               normal: /(auto|scroll)/,
35335               hidden: /(auto|scroll|hidden)/
35336             };
35337             var PLACEMENT_REGEX = {
35338               auto: /\s?auto?\s?/i,
35339               primary: /^(top|bottom|left|right)$/,
35340               secondary: /^(top|bottom|left|right|center)$/,
35341               vertical: /^(top|bottom)$/
35342             };
35343
35344             return {
35345
35346               /**
35347                * Provides a raw DOM element from a jQuery/jQLite element.
35348                *
35349                * @param {element} elem - The element to convert.
35350                *
35351                * @returns {element} A HTML element.
35352                */
35353               getRawNode: function(elem) {
35354                 return elem[0] || elem;
35355               },
35356
35357               /**
35358                * Provides a parsed number for a style property.  Strips
35359                * units and casts invalid numbers to 0.
35360                *
35361                * @param {string} value - The style value to parse.
35362                *
35363                * @returns {number} A valid number.
35364                */
35365               parseStyle: function(value) {
35366                 value = parseFloat(value);
35367                 return isFinite(value) ? value : 0;
35368               },
35369
35370               /**
35371                * Provides the closest positioned ancestor.
35372                *
35373                * @param {element} element - The element to get the offest parent for.
35374                *
35375                * @returns {element} The closest positioned ancestor.
35376                */
35377               offsetParent: function(elem) {
35378                 elem = this.getRawNode(elem);
35379
35380                 var offsetParent = elem.offsetParent || $document[0].documentElement;
35381
35382                 function isStaticPositioned(el) {
35383                   return ($window.getComputedStyle(el).position || 'static') === 'static';
35384                 }
35385
35386                 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
35387                   offsetParent = offsetParent.offsetParent;
35388                 }
35389
35390                 return offsetParent || $document[0].documentElement;
35391               },
35392
35393               /**
35394                * Provides the scrollbar width, concept from TWBS measureScrollbar()
35395                * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
35396                *
35397                * @returns {number} The width of the browser scollbar.
35398                */
35399               scrollbarWidth: function() {
35400                 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
35401                   var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
35402                   $document.find('body').append(scrollElem);
35403                   SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
35404                   SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
35405                   scrollElem.remove();
35406                 }
35407
35408                 return SCROLLBAR_WIDTH;
35409               },
35410
35411               /**
35412                * Provides the closest scrollable ancestor.
35413                * A port of the jQuery UI scrollParent method:
35414                * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
35415                *
35416                * @param {element} elem - The element to find the scroll parent of.
35417                * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
35418                *   default is false.
35419                *
35420                * @returns {element} A HTML element.
35421                */
35422               scrollParent: function(elem, includeHidden) {
35423                 elem = this.getRawNode(elem);
35424
35425                 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
35426                 var documentEl = $document[0].documentElement;
35427                 var elemStyle = $window.getComputedStyle(elem);
35428                 var excludeStatic = elemStyle.position === 'absolute';
35429                 var scrollParent = elem.parentElement || documentEl;
35430
35431                 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
35432                   return documentEl;
35433                 }
35434
35435                 while (scrollParent.parentElement && scrollParent !== documentEl) {
35436                   var spStyle = $window.getComputedStyle(scrollParent);
35437                   if (excludeStatic && spStyle.position !== 'static') {
35438                     excludeStatic = false;
35439                   }
35440
35441                   if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
35442                     break;
35443                   }
35444                   scrollParent = scrollParent.parentElement;
35445                 }
35446
35447                 return scrollParent;
35448               },
35449
35450               /**
35451                * Provides read-only equivalent of jQuery's position function:
35452                * http://api.jquery.com/position/ - distance to closest positioned
35453                * ancestor.  Does not account for margins by default like jQuery position.
35454                *
35455                * @param {element} elem - The element to caclulate the position on.
35456                * @param {boolean=} [includeMargins=false] - Should margins be accounted
35457                * for, default is false.
35458                *
35459                * @returns {object} An object with the following properties:
35460                *   <ul>
35461                *     <li>**width**: the width of the element</li>
35462                *     <li>**height**: the height of the element</li>
35463                *     <li>**top**: distance to top edge of offset parent</li>
35464                *     <li>**left**: distance to left edge of offset parent</li>
35465                *   </ul>
35466                */
35467               position: function(elem, includeMagins) {
35468                 elem = this.getRawNode(elem);
35469
35470                 var elemOffset = this.offset(elem);
35471                 if (includeMagins) {
35472                   var elemStyle = $window.getComputedStyle(elem);
35473                   elemOffset.top -= this.parseStyle(elemStyle.marginTop);
35474                   elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
35475                 }
35476                 var parent = this.offsetParent(elem);
35477                 var parentOffset = {top: 0, left: 0};
35478
35479                 if (parent !== $document[0].documentElement) {
35480                   parentOffset = this.offset(parent);
35481                   parentOffset.top += parent.clientTop - parent.scrollTop;
35482                   parentOffset.left += parent.clientLeft - parent.scrollLeft;
35483                 }
35484
35485                 return {
35486                   width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
35487                   height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
35488                   top: Math.round(elemOffset.top - parentOffset.top),
35489                   left: Math.round(elemOffset.left - parentOffset.left)
35490                 };
35491               },
35492
35493               /**
35494                * Provides read-only equivalent of jQuery's offset function:
35495                * http://api.jquery.com/offset/ - distance to viewport.  Does
35496                * not account for borders, margins, or padding on the body
35497                * element.
35498                *
35499                * @param {element} elem - The element to calculate the offset on.
35500                *
35501                * @returns {object} An object with the following properties:
35502                *   <ul>
35503                *     <li>**width**: the width of the element</li>
35504                *     <li>**height**: the height of the element</li>
35505                *     <li>**top**: distance to top edge of viewport</li>
35506                *     <li>**right**: distance to bottom edge of viewport</li>
35507                *   </ul>
35508                */
35509               offset: function(elem) {
35510                 elem = this.getRawNode(elem);
35511
35512                 var elemBCR = elem.getBoundingClientRect();
35513                 return {
35514                   width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
35515                   height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
35516                   top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
35517                   left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
35518                 };
35519               },
35520
35521               /**
35522                * Provides offset distance to the closest scrollable ancestor
35523                * or viewport.  Accounts for border and scrollbar width.
35524                *
35525                * Right and bottom dimensions represent the distance to the
35526                * respective edge of the viewport element.  If the element
35527                * edge extends beyond the viewport, a negative value will be
35528                * reported.
35529                *
35530                * @param {element} elem - The element to get the viewport offset for.
35531                * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
35532                * of the first scrollable element, default is false.
35533                * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
35534                * be accounted for, default is true.
35535                *
35536                * @returns {object} An object with the following properties:
35537                *   <ul>
35538                *     <li>**top**: distance to the top content edge of viewport element</li>
35539                *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
35540                *     <li>**left**: distance to the left content edge of viewport element</li>
35541                *     <li>**right**: distance to the right content edge of viewport element</li>
35542                *   </ul>
35543                */
35544               viewportOffset: function(elem, useDocument, includePadding) {
35545                 elem = this.getRawNode(elem);
35546                 includePadding = includePadding !== false ? true : false;
35547
35548                 var elemBCR = elem.getBoundingClientRect();
35549                 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
35550
35551                 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
35552                 var offsetParentBCR = offsetParent.getBoundingClientRect();
35553
35554                 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
35555                 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
35556                 if (offsetParent === $document[0].documentElement) {
35557                   offsetBCR.top += $window.pageYOffset;
35558                   offsetBCR.left += $window.pageXOffset;
35559                 }
35560                 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
35561                 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
35562
35563                 if (includePadding) {
35564                   var offsetParentStyle = $window.getComputedStyle(offsetParent);
35565                   offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
35566                   offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
35567                   offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
35568                   offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
35569                 }
35570
35571                 return {
35572                   top: Math.round(elemBCR.top - offsetBCR.top),
35573                   bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
35574                   left: Math.round(elemBCR.left - offsetBCR.left),
35575                   right: Math.round(offsetBCR.right - elemBCR.right)
35576                 };
35577               },
35578
35579               /**
35580                * Provides an array of placement values parsed from a placement string.
35581                * Along with the 'auto' indicator, supported placement strings are:
35582                *   <ul>
35583                *     <li>top: element on top, horizontally centered on host element.</li>
35584                *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
35585                *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
35586                *     <li>bottom: element on bottom, horizontally centered on host element.</li>
35587                *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
35588                *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
35589                *     <li>left: element on left, vertically centered on host element.</li>
35590                *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
35591                *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
35592                *     <li>right: element on right, vertically centered on host element.</li>
35593                *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
35594                *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
35595                *   </ul>
35596                * A placement string with an 'auto' indicator is expected to be
35597                * space separated from the placement, i.e: 'auto bottom-left'  If
35598                * the primary and secondary placement values do not match 'top,
35599                * bottom, left, right' then 'top' will be the primary placement and
35600                * 'center' will be the secondary placement.  If 'auto' is passed, true
35601                * will be returned as the 3rd value of the array.
35602                *
35603                * @param {string} placement - The placement string to parse.
35604                *
35605                * @returns {array} An array with the following values
35606                * <ul>
35607                *   <li>**[0]**: The primary placement.</li>
35608                *   <li>**[1]**: The secondary placement.</li>
35609                *   <li>**[2]**: If auto is passed: true, else undefined.</li>
35610                * </ul>
35611                */
35612               parsePlacement: function(placement) {
35613                 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
35614                 if (autoPlace) {
35615                   placement = placement.replace(PLACEMENT_REGEX.auto, '');
35616                 }
35617
35618                 placement = placement.split('-');
35619
35620                 placement[0] = placement[0] || 'top';
35621                 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
35622                   placement[0] = 'top';
35623                 }
35624
35625                 placement[1] = placement[1] || 'center';
35626                 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
35627                   placement[1] = 'center';
35628                 }
35629
35630                 if (autoPlace) {
35631                   placement[2] = true;
35632                 } else {
35633                   placement[2] = false;
35634                 }
35635
35636                 return placement;
35637               },
35638
35639               /**
35640                * Provides coordinates for an element to be positioned relative to
35641                * another element.  Passing 'auto' as part of the placement parameter
35642                * will enable smart placement - where the element fits. i.e:
35643                * 'auto left-top' will check to see if there is enough space to the left
35644                * of the hostElem to fit the targetElem, if not place right (same for secondary
35645                * top placement).  Available space is calculated using the viewportOffset
35646                * function.
35647                *
35648                * @param {element} hostElem - The element to position against.
35649                * @param {element} targetElem - The element to position.
35650                * @param {string=} [placement=top] - The placement for the targetElem,
35651                *   default is 'top'. 'center' is assumed as secondary placement for
35652                *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
35653                *   <ul>
35654                *     <li>top</li>
35655                *     <li>top-right</li>
35656                *     <li>top-left</li>
35657                *     <li>bottom</li>
35658                *     <li>bottom-left</li>
35659                *     <li>bottom-right</li>
35660                *     <li>left</li>
35661                *     <li>left-top</li>
35662                *     <li>left-bottom</li>
35663                *     <li>right</li>
35664                *     <li>right-top</li>
35665                *     <li>right-bottom</li>
35666                *   </ul>
35667                * @param {boolean=} [appendToBody=false] - Should the top and left values returned
35668                *   be calculated from the body element, default is false.
35669                *
35670                * @returns {object} An object with the following properties:
35671                *   <ul>
35672                *     <li>**top**: Value for targetElem top.</li>
35673                *     <li>**left**: Value for targetElem left.</li>
35674                *     <li>**placement**: The resolved placement.</li>
35675                *   </ul>
35676                */
35677               positionElements: function(hostElem, targetElem, placement, appendToBody) {
35678                 hostElem = this.getRawNode(hostElem);
35679                 targetElem = this.getRawNode(targetElem);
35680
35681                 // need to read from prop to support tests.
35682                 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
35683                 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
35684
35685                 placement = this.parsePlacement(placement);
35686
35687                 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
35688                 var targetElemPos = {top: 0, left: 0, placement: ''};
35689
35690                 if (placement[2]) {
35691                   var viewportOffset = this.viewportOffset(hostElem);
35692
35693                   var targetElemStyle = $window.getComputedStyle(targetElem);
35694                   var adjustedSize = {
35695                     width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
35696                     height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
35697                   };
35698
35699                   placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
35700                                  placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
35701                                  placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
35702                                  placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
35703                                  placement[0];
35704
35705                   placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
35706                                  placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
35707                                  placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
35708                                  placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
35709                                  placement[1];
35710
35711                   if (placement[1] === 'center') {
35712                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35713                       var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
35714                       if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
35715                         placement[1] = 'left';
35716                       } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
35717                         placement[1] = 'right';
35718                       }
35719                     } else {
35720                       var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
35721                       if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
35722                         placement[1] = 'top';
35723                       } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
35724                         placement[1] = 'bottom';
35725                       }
35726                     }
35727                   }
35728                 }
35729
35730                 switch (placement[0]) {
35731                   case 'top':
35732                     targetElemPos.top = hostElemPos.top - targetHeight;
35733                     break;
35734                   case 'bottom':
35735                     targetElemPos.top = hostElemPos.top + hostElemPos.height;
35736                     break;
35737                   case 'left':
35738                     targetElemPos.left = hostElemPos.left - targetWidth;
35739                     break;
35740                   case 'right':
35741                     targetElemPos.left = hostElemPos.left + hostElemPos.width;
35742                     break;
35743                 }
35744
35745                 switch (placement[1]) {
35746                   case 'top':
35747                     targetElemPos.top = hostElemPos.top;
35748                     break;
35749                   case 'bottom':
35750                     targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
35751                     break;
35752                   case 'left':
35753                     targetElemPos.left = hostElemPos.left;
35754                     break;
35755                   case 'right':
35756                     targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
35757                     break;
35758                   case 'center':
35759                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35760                       targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
35761                     } else {
35762                       targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
35763                     }
35764                     break;
35765                 }
35766
35767                 targetElemPos.top = Math.round(targetElemPos.top);
35768                 targetElemPos.left = Math.round(targetElemPos.left);
35769                 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
35770
35771                 return targetElemPos;
35772               },
35773
35774               /**
35775               * Provides a way for positioning tooltip & dropdown
35776               * arrows when using placement options beyond the standard
35777               * left, right, top, or bottom.
35778               *
35779               * @param {element} elem - The tooltip/dropdown element.
35780               * @param {string} placement - The placement for the elem.
35781               */
35782               positionArrow: function(elem, placement) {
35783                 elem = this.getRawNode(elem);
35784
35785                 var isTooltip = true;
35786
35787                 var innerElem = elem.querySelector('.tooltip-inner');
35788                 if (!innerElem) {
35789                   isTooltip = false;
35790                   innerElem = elem.querySelector('.popover-inner');
35791                 }
35792                 if (!innerElem) {
35793                   return;
35794                 }
35795
35796                 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
35797                 if (!arrowElem) {
35798                   return;
35799                 }
35800
35801                 placement = this.parsePlacement(placement);
35802                 if (placement[1] === 'center') {
35803                   // no adjustment necessary - just reset styles
35804                   angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
35805                   return;
35806                 }
35807
35808                 var borderProp = 'border-' + placement[0] + '-width';
35809                 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
35810
35811                 var borderRadiusProp = 'border-';
35812                 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35813                   borderRadiusProp += placement[0] + '-' + placement[1];
35814                 } else {
35815                   borderRadiusProp += placement[1] + '-' + placement[0];
35816                 }
35817                 borderRadiusProp += '-radius';
35818                 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
35819
35820                 var arrowCss = {
35821                   top: 'auto',
35822                   bottom: 'auto',
35823                   left: 'auto',
35824                   right: 'auto',
35825                   margin: 0
35826                 };
35827
35828                 switch (placement[0]) {
35829                   case 'top':
35830                     arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
35831                     break;
35832                   case 'bottom':
35833                     arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
35834                     break;
35835                   case 'left':
35836                     arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
35837                     break;
35838                   case 'right':
35839                     arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
35840                     break;
35841                 }
35842
35843                 arrowCss[placement[1]] = borderRadius;
35844
35845                 angular.element(arrowElem).css(arrowCss);
35846               }
35847             };
35848           }]);
35849
35850         angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position'])
35851
35852         .value('$datepickerSuppressError', false)
35853
35854         .constant('uibDatepickerConfig', {
35855           formatDay: 'dd',
35856           formatMonth: 'MMMM',
35857           formatYear: 'yyyy',
35858           formatDayHeader: 'EEE',
35859           formatDayTitle: 'MMMM yyyy',
35860           formatMonthTitle: 'yyyy',
35861           datepickerMode: 'day',
35862           minMode: 'day',
35863           maxMode: 'year',
35864           showWeeks: true,
35865           startingDay: 0,
35866           yearRows: 4,
35867           yearColumns: 5,
35868           minDate: null,
35869           maxDate: null,
35870           shortcutPropagation: false,
35871           ngModelOptions: {}
35872         })
35873
35874         .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser',
35875           function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) {
35876           var self = this,
35877               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
35878               ngModelOptions = {};
35879
35880           // Modes chain
35881           this.modes = ['day', 'month', 'year'];
35882
35883           // Interpolated configuration attributes
35884           angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) {
35885             self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key];
35886           });
35887
35888           // Evaled configuration attributes
35889           angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) {
35890             self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key];
35891           });
35892
35893           // Watchable date attributes
35894           angular.forEach(['minDate', 'maxDate'], function(key) {
35895             if ($attrs[key]) {
35896               $scope.$parent.$watch($attrs[key], function(value) {
35897                 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null;
35898                 self.refreshView();
35899               });
35900             } else {
35901               self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null;
35902             }
35903           });
35904
35905           angular.forEach(['minMode', 'maxMode'], function(key) {
35906             if ($attrs[key]) {
35907               $scope.$parent.$watch($attrs[key], function(value) {
35908                 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key];
35909                 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) ||
35910                   key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) {
35911                   $scope.datepickerMode = self[key];
35912                 }
35913               });
35914             } else {
35915               self[key] = $scope[key] = datepickerConfig[key] || null;
35916             }
35917           });
35918
35919           $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
35920           $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
35921
35922           if (angular.isDefined($attrs.initDate)) {
35923             this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date();
35924             $scope.$parent.$watch($attrs.initDate, function(initDate) {
35925               if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
35926                 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
35927                 self.refreshView();
35928               }
35929             });
35930           } else {
35931             this.activeDate = new Date();
35932           }
35933
35934           $scope.disabled = angular.isDefined($attrs.disabled) || false;
35935           if (angular.isDefined($attrs.ngDisabled)) {
35936             $scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
35937               $scope.disabled = disabled;
35938               self.refreshView();
35939             });
35940           }
35941
35942           $scope.isActive = function(dateObject) {
35943             if (self.compare(dateObject.date, self.activeDate) === 0) {
35944               $scope.activeDateId = dateObject.uid;
35945               return true;
35946             }
35947             return false;
35948           };
35949
35950           this.init = function(ngModelCtrl_) {
35951             ngModelCtrl = ngModelCtrl_;
35952             ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
35953
35954             if (ngModelCtrl.$modelValue) {
35955               this.activeDate = ngModelCtrl.$modelValue;
35956             }
35957
35958             ngModelCtrl.$render = function() {
35959               self.render();
35960             };
35961           };
35962
35963           this.render = function() {
35964             if (ngModelCtrl.$viewValue) {
35965               var date = new Date(ngModelCtrl.$viewValue),
35966                   isValid = !isNaN(date);
35967
35968               if (isValid) {
35969                 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
35970               } else if (!$datepickerSuppressError) {
35971                 $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.');
35972               }
35973             }
35974             this.refreshView();
35975           };
35976
35977           this.refreshView = function() {
35978             if (this.element) {
35979               $scope.selectedDt = null;
35980               this._refreshView();
35981               if ($scope.activeDt) {
35982                 $scope.activeDateId = $scope.activeDt.uid;
35983               }
35984
35985               var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35986               date = dateParser.fromTimezone(date, ngModelOptions.timezone);
35987               ngModelCtrl.$setValidity('dateDisabled', !date ||
35988                 this.element && !this.isDisabled(date));
35989             }
35990           };
35991
35992           this.createDateObject = function(date, format) {
35993             var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35994             model = dateParser.fromTimezone(model, ngModelOptions.timezone);
35995             var dt = {
35996               date: date,
35997               label: dateFilter(date, format),
35998               selected: model && this.compare(date, model) === 0,
35999               disabled: this.isDisabled(date),
36000               current: this.compare(date, new Date()) === 0,
36001               customClass: this.customClass(date) || null
36002             };
36003
36004             if (model && this.compare(date, model) === 0) {
36005               $scope.selectedDt = dt;
36006             }
36007
36008             if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
36009               $scope.activeDt = dt;
36010             }
36011
36012             return dt;
36013           };
36014
36015           this.isDisabled = function(date) {
36016             return $scope.disabled ||
36017               this.minDate && this.compare(date, this.minDate) < 0 ||
36018               this.maxDate && this.compare(date, this.maxDate) > 0 ||
36019               $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
36020           };
36021
36022           this.customClass = function(date) {
36023             return $scope.customClass({date: date, mode: $scope.datepickerMode});
36024           };
36025
36026           // Split array into smaller arrays
36027           this.split = function(arr, size) {
36028             var arrays = [];
36029             while (arr.length > 0) {
36030               arrays.push(arr.splice(0, size));
36031             }
36032             return arrays;
36033           };
36034
36035           $scope.select = function(date) {
36036             if ($scope.datepickerMode === self.minMode) {
36037               var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
36038               dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
36039               dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
36040               ngModelCtrl.$setViewValue(dt);
36041               ngModelCtrl.$render();
36042             } else {
36043               self.activeDate = date;
36044               $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
36045             }
36046           };
36047
36048           $scope.move = function(direction) {
36049             var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
36050                 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
36051             self.activeDate.setFullYear(year, month, 1);
36052             self.refreshView();
36053           };
36054
36055           $scope.toggleMode = function(direction) {
36056             direction = direction || 1;
36057
36058             if ($scope.datepickerMode === self.maxMode && direction === 1 ||
36059               $scope.datepickerMode === self.minMode && direction === -1) {
36060               return;
36061             }
36062
36063             $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
36064           };
36065
36066           // Key event mapper
36067           $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
36068
36069           var focusElement = function() {
36070             self.element[0].focus();
36071           };
36072
36073           // Listen for focus requests from popup directive
36074           $scope.$on('uib:datepicker.focus', focusElement);
36075
36076           $scope.keydown = function(evt) {
36077             var key = $scope.keys[evt.which];
36078
36079             if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
36080               return;
36081             }
36082
36083             evt.preventDefault();
36084             if (!self.shortcutPropagation) {
36085               evt.stopPropagation();
36086             }
36087
36088             if (key === 'enter' || key === 'space') {
36089               if (self.isDisabled(self.activeDate)) {
36090                 return; // do nothing
36091               }
36092               $scope.select(self.activeDate);
36093             } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
36094               $scope.toggleMode(key === 'up' ? 1 : -1);
36095             } else {
36096               self.handleKeyDown(key, evt);
36097               self.refreshView();
36098             }
36099           };
36100         }])
36101
36102         .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36103           var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
36104
36105           this.step = { months: 1 };
36106           this.element = $element;
36107           function getDaysInMonth(year, month) {
36108             return month === 1 && year % 4 === 0 &&
36109               (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
36110           }
36111
36112           this.init = function(ctrl) {
36113             angular.extend(ctrl, this);
36114             scope.showWeeks = ctrl.showWeeks;
36115             ctrl.refreshView();
36116           };
36117
36118           this.getDates = function(startDate, n) {
36119             var dates = new Array(n), current = new Date(startDate), i = 0, date;
36120             while (i < n) {
36121               date = new Date(current);
36122               dates[i++] = date;
36123               current.setDate(current.getDate() + 1);
36124             }
36125             return dates;
36126           };
36127
36128           this._refreshView = function() {
36129             var year = this.activeDate.getFullYear(),
36130               month = this.activeDate.getMonth(),
36131               firstDayOfMonth = new Date(this.activeDate);
36132
36133             firstDayOfMonth.setFullYear(year, month, 1);
36134
36135             var difference = this.startingDay - firstDayOfMonth.getDay(),
36136               numDisplayedFromPreviousMonth = difference > 0 ?
36137                 7 - difference : - difference,
36138               firstDate = new Date(firstDayOfMonth);
36139
36140             if (numDisplayedFromPreviousMonth > 0) {
36141               firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
36142             }
36143
36144             // 42 is the number of days on a six-week calendar
36145             var days = this.getDates(firstDate, 42);
36146             for (var i = 0; i < 42; i ++) {
36147               days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
36148                 secondary: days[i].getMonth() !== month,
36149                 uid: scope.uniqueId + '-' + i
36150               });
36151             }
36152
36153             scope.labels = new Array(7);
36154             for (var j = 0; j < 7; j++) {
36155               scope.labels[j] = {
36156                 abbr: dateFilter(days[j].date, this.formatDayHeader),
36157                 full: dateFilter(days[j].date, 'EEEE')
36158               };
36159             }
36160
36161             scope.title = dateFilter(this.activeDate, this.formatDayTitle);
36162             scope.rows = this.split(days, 7);
36163
36164             if (scope.showWeeks) {
36165               scope.weekNumbers = [];
36166               var thursdayIndex = (4 + 7 - this.startingDay) % 7,
36167                   numWeeks = scope.rows.length;
36168               for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
36169                 scope.weekNumbers.push(
36170                   getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
36171               }
36172             }
36173           };
36174
36175           this.compare = function(date1, date2) {
36176             var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
36177             var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36178             _date1.setFullYear(date1.getFullYear());
36179             _date2.setFullYear(date2.getFullYear());
36180             return _date1 - _date2;
36181           };
36182
36183           function getISO8601WeekNumber(date) {
36184             var checkDate = new Date(date);
36185             checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
36186             var time = checkDate.getTime();
36187             checkDate.setMonth(0); // Compare with Jan 1
36188             checkDate.setDate(1);
36189             return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
36190           }
36191
36192           this.handleKeyDown = function(key, evt) {
36193             var date = this.activeDate.getDate();
36194
36195             if (key === 'left') {
36196               date = date - 1;
36197             } else if (key === 'up') {
36198               date = date - 7;
36199             } else if (key === 'right') {
36200               date = date + 1;
36201             } else if (key === 'down') {
36202               date = date + 7;
36203             } else if (key === 'pageup' || key === 'pagedown') {
36204               var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
36205               this.activeDate.setMonth(month, 1);
36206               date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
36207             } else if (key === 'home') {
36208               date = 1;
36209             } else if (key === 'end') {
36210               date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
36211             }
36212             this.activeDate.setDate(date);
36213           };
36214         }])
36215
36216         .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36217           this.step = { years: 1 };
36218           this.element = $element;
36219
36220           this.init = function(ctrl) {
36221             angular.extend(ctrl, this);
36222             ctrl.refreshView();
36223           };
36224
36225           this._refreshView = function() {
36226             var months = new Array(12),
36227                 year = this.activeDate.getFullYear(),
36228                 date;
36229
36230             for (var i = 0; i < 12; i++) {
36231               date = new Date(this.activeDate);
36232               date.setFullYear(year, i, 1);
36233               months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
36234                 uid: scope.uniqueId + '-' + i
36235               });
36236             }
36237
36238             scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
36239             scope.rows = this.split(months, 3);
36240           };
36241
36242           this.compare = function(date1, date2) {
36243             var _date1 = new Date(date1.getFullYear(), date1.getMonth());
36244             var _date2 = new Date(date2.getFullYear(), date2.getMonth());
36245             _date1.setFullYear(date1.getFullYear());
36246             _date2.setFullYear(date2.getFullYear());
36247             return _date1 - _date2;
36248           };
36249
36250           this.handleKeyDown = function(key, evt) {
36251             var date = this.activeDate.getMonth();
36252
36253             if (key === 'left') {
36254               date = date - 1;
36255             } else if (key === 'up') {
36256               date = date - 3;
36257             } else if (key === 'right') {
36258               date = date + 1;
36259             } else if (key === 'down') {
36260               date = date + 3;
36261             } else if (key === 'pageup' || key === 'pagedown') {
36262               var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
36263               this.activeDate.setFullYear(year);
36264             } else if (key === 'home') {
36265               date = 0;
36266             } else if (key === 'end') {
36267               date = 11;
36268             }
36269             this.activeDate.setMonth(date);
36270           };
36271         }])
36272
36273         .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36274           var columns, range;
36275           this.element = $element;
36276
36277           function getStartingYear(year) {
36278             return parseInt((year - 1) / range, 10) * range + 1;
36279           }
36280
36281           this.yearpickerInit = function() {
36282             columns = this.yearColumns;
36283             range = this.yearRows * columns;
36284             this.step = { years: range };
36285           };
36286
36287           this._refreshView = function() {
36288             var years = new Array(range), date;
36289
36290             for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
36291               date = new Date(this.activeDate);
36292               date.setFullYear(start + i, 0, 1);
36293               years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
36294                 uid: scope.uniqueId + '-' + i
36295               });
36296             }
36297
36298             scope.title = [years[0].label, years[range - 1].label].join(' - ');
36299             scope.rows = this.split(years, columns);
36300             scope.columns = columns;
36301           };
36302
36303           this.compare = function(date1, date2) {
36304             return date1.getFullYear() - date2.getFullYear();
36305           };
36306
36307           this.handleKeyDown = function(key, evt) {
36308             var date = this.activeDate.getFullYear();
36309
36310             if (key === 'left') {
36311               date = date - 1;
36312             } else if (key === 'up') {
36313               date = date - columns;
36314             } else if (key === 'right') {
36315               date = date + 1;
36316             } else if (key === 'down') {
36317               date = date + columns;
36318             } else if (key === 'pageup' || key === 'pagedown') {
36319               date += (key === 'pageup' ? - 1 : 1) * range;
36320             } else if (key === 'home') {
36321               date = getStartingYear(this.activeDate.getFullYear());
36322             } else if (key === 'end') {
36323               date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
36324             }
36325             this.activeDate.setFullYear(date);
36326           };
36327         }])
36328
36329         .directive('uibDatepicker', function() {
36330           return {
36331             replace: true,
36332             templateUrl: function(element, attrs) {
36333               return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
36334             },
36335             scope: {
36336               datepickerMode: '=?',
36337               dateDisabled: '&',
36338               customClass: '&',
36339               shortcutPropagation: '&?'
36340             },
36341             require: ['uibDatepicker', '^ngModel'],
36342             controller: 'UibDatepickerController',
36343             controllerAs: 'datepicker',
36344             link: function(scope, element, attrs, ctrls) {
36345               var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
36346
36347               datepickerCtrl.init(ngModelCtrl);
36348             }
36349           };
36350         })
36351
36352         .directive('uibDaypicker', function() {
36353           return {
36354             replace: true,
36355             templateUrl: function(element, attrs) {
36356               return attrs.templateUrl || 'uib/template/datepicker/day.html';
36357             },
36358             require: ['^uibDatepicker', 'uibDaypicker'],
36359             controller: 'UibDaypickerController',
36360             link: function(scope, element, attrs, ctrls) {
36361               var datepickerCtrl = ctrls[0],
36362                 daypickerCtrl = ctrls[1];
36363
36364               daypickerCtrl.init(datepickerCtrl);
36365             }
36366           };
36367         })
36368
36369         .directive('uibMonthpicker', function() {
36370           return {
36371             replace: true,
36372             templateUrl: function(element, attrs) {
36373               return attrs.templateUrl || 'uib/template/datepicker/month.html';
36374             },
36375             require: ['^uibDatepicker', 'uibMonthpicker'],
36376             controller: 'UibMonthpickerController',
36377             link: function(scope, element, attrs, ctrls) {
36378               var datepickerCtrl = ctrls[0],
36379                 monthpickerCtrl = ctrls[1];
36380
36381               monthpickerCtrl.init(datepickerCtrl);
36382             }
36383           };
36384         })
36385
36386         .directive('uibYearpicker', function() {
36387           return {
36388             replace: true,
36389             templateUrl: function(element, attrs) {
36390               return attrs.templateUrl || 'uib/template/datepicker/year.html';
36391             },
36392             require: ['^uibDatepicker', 'uibYearpicker'],
36393             controller: 'UibYearpickerController',
36394             link: function(scope, element, attrs, ctrls) {
36395               var ctrl = ctrls[0];
36396               angular.extend(ctrl, ctrls[1]);
36397               ctrl.yearpickerInit();
36398
36399               ctrl.refreshView();
36400             }
36401           };
36402         })
36403
36404         .constant('uibDatepickerPopupConfig', {
36405           datepickerPopup: 'yyyy-MM-dd',
36406           datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html',
36407           datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
36408           html5Types: {
36409             date: 'yyyy-MM-dd',
36410             'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
36411             'month': 'yyyy-MM'
36412           },
36413           currentText: 'Today',
36414           clearText: 'Clear',
36415           closeText: 'Done',
36416           closeOnDateSelection: true,
36417           appendToBody: false,
36418           showButtonBar: true,
36419           onOpenFocus: true,
36420           altInputFormats: []
36421         })
36422
36423         .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig',
36424         function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) {
36425           var self = this;
36426           var cache = {},
36427             isHtml5DateInput = false;
36428           var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
36429             datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
36430             ngModel, ngModelOptions, $popup, altInputFormats;
36431
36432           scope.watchData = {};
36433
36434           this.init = function(_ngModel_) {
36435             ngModel = _ngModel_;
36436             ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions;
36437             closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
36438             appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
36439             onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
36440             datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
36441             datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
36442             altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
36443
36444             scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
36445
36446             if (datepickerPopupConfig.html5Types[attrs.type]) {
36447               dateFormat = datepickerPopupConfig.html5Types[attrs.type];
36448               isHtml5DateInput = true;
36449             } else {
36450               dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
36451               attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
36452                   var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
36453                   // Invalidate the $modelValue to ensure that formatters re-run
36454                   // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
36455                   if (newDateFormat !== dateFormat) {
36456                     dateFormat = newDateFormat;
36457                     ngModel.$modelValue = null;
36458
36459                     if (!dateFormat) {
36460                       throw new Error('uibDatepickerPopup must have a date format specified.');
36461                     }
36462                   }
36463               });
36464             }
36465
36466             if (!dateFormat) {
36467               throw new Error('uibDatepickerPopup must have a date format specified.');
36468             }
36469
36470             if (isHtml5DateInput && attrs.uibDatepickerPopup) {
36471               throw new Error('HTML5 date input types do not support custom formats.');
36472             }
36473
36474             // popup element used to display calendar
36475             popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
36476             scope.ngModelOptions = angular.copy(ngModelOptions);
36477             scope.ngModelOptions.timezone = null;
36478             popupEl.attr({
36479               'ng-model': 'date',
36480               'ng-model-options': 'ngModelOptions',
36481               'ng-change': 'dateSelection(date)',
36482               'template-url': datepickerPopupTemplateUrl
36483             });
36484
36485             // datepicker element
36486             datepickerEl = angular.element(popupEl.children()[0]);
36487             datepickerEl.attr('template-url', datepickerTemplateUrl);
36488
36489             if (isHtml5DateInput) {
36490               if (attrs.type === 'month') {
36491                 datepickerEl.attr('datepicker-mode', '"month"');
36492                 datepickerEl.attr('min-mode', 'month');
36493               }
36494             }
36495
36496             if (attrs.datepickerOptions) {
36497               var options = scope.$parent.$eval(attrs.datepickerOptions);
36498               if (options && options.initDate) {
36499                 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone);
36500                 datepickerEl.attr('init-date', 'initDate');
36501                 delete options.initDate;
36502               }
36503               angular.forEach(options, function(value, option) {
36504                 datepickerEl.attr(cameltoDash(option), value);
36505               });
36506             }
36507
36508             angular.forEach(['minMode', 'maxMode'], function(key) {
36509               if (attrs[key]) {
36510                 scope.$parent.$watch(function() { return attrs[key]; }, function(value) {
36511                   scope.watchData[key] = value;
36512                 });
36513                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36514               }
36515             });
36516
36517             angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) {
36518               if (attrs[key]) {
36519                 var getAttribute = $parse(attrs[key]);
36520                 var propConfig = {
36521                   get: function() {
36522                     return getAttribute(scope.$parent);
36523                   }
36524                 };
36525
36526                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36527
36528                 // Propagate changes from datepicker to outside
36529                 if (key === 'datepickerMode') {
36530                   var setAttribute = getAttribute.assign;
36531                   propConfig.set = function(v) {
36532                     setAttribute(scope.$parent, v);
36533                   };
36534                 }
36535
36536                 Object.defineProperty(scope.watchData, key, propConfig);
36537               }
36538             });
36539
36540             angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) {
36541               if (attrs[key]) {
36542                 var getAttribute = $parse(attrs[key]);
36543
36544                 scope.$parent.$watch(getAttribute, function(value) {
36545                   if (key === 'minDate' || key === 'maxDate') {
36546                     cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium'));
36547                   }
36548
36549                   scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
36550                 });
36551
36552                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36553               }
36554             });
36555
36556             if (attrs.dateDisabled) {
36557               datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
36558             }
36559
36560             angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) {
36561               if (angular.isDefined(attrs[key])) {
36562                 datepickerEl.attr(cameltoDash(key), attrs[key]);
36563               }
36564             });
36565
36566             if (attrs.customClass) {
36567               datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
36568             }
36569
36570             if (!isHtml5DateInput) {
36571               // Internal API to maintain the correct ng-invalid-[key] class
36572               ngModel.$$parserName = 'date';
36573               ngModel.$validators.date = validator;
36574               ngModel.$parsers.unshift(parseDate);
36575               ngModel.$formatters.push(function(value) {
36576                 if (ngModel.$isEmpty(value)) {
36577                   scope.date = value;
36578                   return value;
36579                 }
36580                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36581                 return dateFilter(scope.date, dateFormat);
36582               });
36583             } else {
36584               ngModel.$formatters.push(function(value) {
36585                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36586                 return value;
36587               });
36588             }
36589
36590             // Detect changes in the view from the text box
36591             ngModel.$viewChangeListeners.push(function() {
36592               scope.date = parseDateString(ngModel.$viewValue);
36593             });
36594
36595             element.bind('keydown', inputKeydownBind);
36596
36597             $popup = $compile(popupEl)(scope);
36598             // Prevent jQuery cache memory leak (template is now redundant after linking)
36599             popupEl.remove();
36600
36601             if (appendToBody) {
36602               $document.find('body').append($popup);
36603             } else {
36604               element.after($popup);
36605             }
36606
36607             scope.$on('$destroy', function() {
36608               if (scope.isOpen === true) {
36609                 if (!$rootScope.$$phase) {
36610                   scope.$apply(function() {
36611                     scope.isOpen = false;
36612                   });
36613                 }
36614               }
36615
36616               $popup.remove();
36617               element.unbind('keydown', inputKeydownBind);
36618               $document.unbind('click', documentClickBind);
36619             });
36620           };
36621
36622           scope.getText = function(key) {
36623             return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
36624           };
36625
36626           scope.isDisabled = function(date) {
36627             if (date === 'today') {
36628               date = new Date();
36629             }
36630
36631             return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 ||
36632               scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0;
36633           };
36634
36635           scope.compare = function(date1, date2) {
36636             return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36637           };
36638
36639           // Inner change
36640           scope.dateSelection = function(dt) {
36641             if (angular.isDefined(dt)) {
36642               scope.date = dt;
36643             }
36644             var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
36645             element.val(date);
36646             ngModel.$setViewValue(date);
36647
36648             if (closeOnDateSelection) {
36649               scope.isOpen = false;
36650               element[0].focus();
36651             }
36652           };
36653
36654           scope.keydown = function(evt) {
36655             if (evt.which === 27) {
36656               evt.stopPropagation();
36657               scope.isOpen = false;
36658               element[0].focus();
36659             }
36660           };
36661
36662           scope.select = function(date) {
36663             if (date === 'today') {
36664               var today = new Date();
36665               if (angular.isDate(scope.date)) {
36666                 date = new Date(scope.date);
36667                 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
36668               } else {
36669                 date = new Date(today.setHours(0, 0, 0, 0));
36670               }
36671             }
36672             scope.dateSelection(date);
36673           };
36674
36675           scope.close = function() {
36676             scope.isOpen = false;
36677             element[0].focus();
36678           };
36679
36680           scope.disabled = angular.isDefined(attrs.disabled) || false;
36681           if (attrs.ngDisabled) {
36682             scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) {
36683               scope.disabled = disabled;
36684             });
36685           }
36686
36687           scope.$watch('isOpen', function(value) {
36688             if (value) {
36689               if (!scope.disabled) {
36690                 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
36691                 scope.position.top = scope.position.top + element.prop('offsetHeight');
36692
36693                 $timeout(function() {
36694                   if (onOpenFocus) {
36695                     scope.$broadcast('uib:datepicker.focus');
36696                   }
36697                   $document.bind('click', documentClickBind);
36698                 }, 0, false);
36699               } else {
36700                 scope.isOpen = false;
36701               }
36702             } else {
36703               $document.unbind('click', documentClickBind);
36704             }
36705           });
36706
36707           function cameltoDash(string) {
36708             return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
36709           }
36710
36711           function parseDateString(viewValue) {
36712             var date = dateParser.parse(viewValue, dateFormat, scope.date);
36713             if (isNaN(date)) {
36714               for (var i = 0; i < altInputFormats.length; i++) {
36715                 date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
36716                 if (!isNaN(date)) {
36717                   return date;
36718                 }
36719               }
36720             }
36721             return date;
36722           }
36723
36724           function parseDate(viewValue) {
36725             if (angular.isNumber(viewValue)) {
36726               // presumably timestamp to date object
36727               viewValue = new Date(viewValue);
36728             }
36729
36730             if (!viewValue) {
36731               return null;
36732             }
36733
36734             if (angular.isDate(viewValue) && !isNaN(viewValue)) {
36735               return viewValue;
36736             }
36737
36738             if (angular.isString(viewValue)) {
36739               var date = parseDateString(viewValue);
36740               if (!isNaN(date)) {
36741                 return dateParser.toTimezone(date, ngModelOptions.timezone);
36742               }
36743             }
36744
36745             return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
36746           }
36747
36748           function validator(modelValue, viewValue) {
36749             var value = modelValue || viewValue;
36750
36751             if (!attrs.ngRequired && !value) {
36752               return true;
36753             }
36754
36755             if (angular.isNumber(value)) {
36756               value = new Date(value);
36757             }
36758
36759             if (!value) {
36760               return true;
36761             }
36762
36763             if (angular.isDate(value) && !isNaN(value)) {
36764               return true;
36765             }
36766
36767             if (angular.isString(value)) {
36768               return !isNaN(parseDateString(viewValue));
36769             }
36770
36771             return false;
36772           }
36773
36774           function documentClickBind(event) {
36775             if (!scope.isOpen && scope.disabled) {
36776               return;
36777             }
36778
36779             var popup = $popup[0];
36780             var dpContainsTarget = element[0].contains(event.target);
36781             // The popup node may not be an element node
36782             // In some browsers (IE) only element nodes have the 'contains' function
36783             var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
36784             if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
36785               scope.$apply(function() {
36786                 scope.isOpen = false;
36787               });
36788             }
36789           }
36790
36791           function inputKeydownBind(evt) {
36792             if (evt.which === 27 && scope.isOpen) {
36793               evt.preventDefault();
36794               evt.stopPropagation();
36795               scope.$apply(function() {
36796                 scope.isOpen = false;
36797               });
36798               element[0].focus();
36799             } else if (evt.which === 40 && !scope.isOpen) {
36800               evt.preventDefault();
36801               evt.stopPropagation();
36802               scope.$apply(function() {
36803                 scope.isOpen = true;
36804               });
36805             }
36806           }
36807         }])
36808
36809         .directive('uibDatepickerPopup', function() {
36810           return {
36811             require: ['ngModel', 'uibDatepickerPopup'],
36812             controller: 'UibDatepickerPopupController',
36813             scope: {
36814               isOpen: '=?',
36815               currentText: '@',
36816               clearText: '@',
36817               closeText: '@',
36818               dateDisabled: '&',
36819               customClass: '&'
36820             },
36821             link: function(scope, element, attrs, ctrls) {
36822               var ngModel = ctrls[0],
36823                 ctrl = ctrls[1];
36824
36825               ctrl.init(ngModel);
36826             }
36827           };
36828         })
36829
36830         .directive('uibDatepickerPopupWrap', function() {
36831           return {
36832             replace: true,
36833             transclude: true,
36834             templateUrl: function(element, attrs) {
36835               return attrs.templateUrl || 'uib/template/datepicker/popup.html';
36836             }
36837           };
36838         });
36839
36840         angular.module('ui.bootstrap.debounce', [])
36841         /**
36842          * A helper, internal service that debounces a function
36843          */
36844           .factory('$$debounce', ['$timeout', function($timeout) {
36845             return function(callback, debounceTime) {
36846               var timeoutPromise;
36847
36848               return function() {
36849                 var self = this;
36850                 var args = Array.prototype.slice.call(arguments);
36851                 if (timeoutPromise) {
36852                   $timeout.cancel(timeoutPromise);
36853                 }
36854
36855                 timeoutPromise = $timeout(function() {
36856                   callback.apply(self, args);
36857                 }, debounceTime);
36858               };
36859             };
36860           }]);
36861
36862         angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
36863
36864         .constant('uibDropdownConfig', {
36865           appendToOpenClass: 'uib-dropdown-open',
36866           openClass: 'open'
36867         })
36868
36869         .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
36870           var openScope = null;
36871
36872           this.open = function(dropdownScope) {
36873             if (!openScope) {
36874               $document.on('click', closeDropdown);
36875               $document.on('keydown', keybindFilter);
36876             }
36877
36878             if (openScope && openScope !== dropdownScope) {
36879               openScope.isOpen = false;
36880             }
36881
36882             openScope = dropdownScope;
36883           };
36884
36885           this.close = function(dropdownScope) {
36886             if (openScope === dropdownScope) {
36887               openScope = null;
36888               $document.off('click', closeDropdown);
36889               $document.off('keydown', keybindFilter);
36890             }
36891           };
36892
36893           var closeDropdown = function(evt) {
36894             // This method may still be called during the same mouse event that
36895             // unbound this event handler. So check openScope before proceeding.
36896             if (!openScope) { return; }
36897
36898             if (evt && openScope.getAutoClose() === 'disabled') { return; }
36899
36900             if (evt && evt.which === 3) { return; }
36901
36902             var toggleElement = openScope.getToggleElement();
36903             if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
36904               return;
36905             }
36906
36907             var dropdownElement = openScope.getDropdownElement();
36908             if (evt && openScope.getAutoClose() === 'outsideClick' &&
36909               dropdownElement && dropdownElement[0].contains(evt.target)) {
36910               return;
36911             }
36912
36913             openScope.isOpen = false;
36914
36915             if (!$rootScope.$$phase) {
36916               openScope.$apply();
36917             }
36918           };
36919
36920           var keybindFilter = function(evt) {
36921             if (evt.which === 27) {
36922               openScope.focusToggleElement();
36923               closeDropdown();
36924             } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
36925               evt.preventDefault();
36926               evt.stopPropagation();
36927               openScope.focusDropdownEntry(evt.which);
36928             }
36929           };
36930         }])
36931
36932         .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) {
36933           var self = this,
36934             scope = $scope.$new(), // create a child scope so we are not polluting original one
36935             templateScope,
36936             appendToOpenClass = dropdownConfig.appendToOpenClass,
36937             openClass = dropdownConfig.openClass,
36938             getIsOpen,
36939             setIsOpen = angular.noop,
36940             toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
36941             appendToBody = false,
36942             appendTo = null,
36943             keynavEnabled = false,
36944             selectedOption = null,
36945             body = $document.find('body');
36946
36947           $element.addClass('dropdown');
36948
36949           this.init = function() {
36950             if ($attrs.isOpen) {
36951               getIsOpen = $parse($attrs.isOpen);
36952               setIsOpen = getIsOpen.assign;
36953
36954               $scope.$watch(getIsOpen, function(value) {
36955                 scope.isOpen = !!value;
36956               });
36957             }
36958
36959             if (angular.isDefined($attrs.dropdownAppendTo)) {
36960               var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
36961               if (appendToEl) {
36962                 appendTo = angular.element(appendToEl);
36963               }
36964             }
36965
36966             appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
36967             keynavEnabled = angular.isDefined($attrs.keyboardNav);
36968
36969             if (appendToBody && !appendTo) {
36970               appendTo = body;
36971             }
36972
36973             if (appendTo && self.dropdownMenu) {
36974               appendTo.append(self.dropdownMenu);
36975               $element.on('$destroy', function handleDestroyEvent() {
36976                 self.dropdownMenu.remove();
36977               });
36978             }
36979           };
36980
36981           this.toggle = function(open) {
36982             return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
36983           };
36984
36985           // Allow other directives to watch status
36986           this.isOpen = function() {
36987             return scope.isOpen;
36988           };
36989
36990           scope.getToggleElement = function() {
36991             return self.toggleElement;
36992           };
36993
36994           scope.getAutoClose = function() {
36995             return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
36996           };
36997
36998           scope.getElement = function() {
36999             return $element;
37000           };
37001
37002           scope.isKeynavEnabled = function() {
37003             return keynavEnabled;
37004           };
37005
37006           scope.focusDropdownEntry = function(keyCode) {
37007             var elems = self.dropdownMenu ? //If append to body is used.
37008               angular.element(self.dropdownMenu).find('a') :
37009               $element.find('ul').eq(0).find('a');
37010
37011             switch (keyCode) {
37012               case 40: {
37013                 if (!angular.isNumber(self.selectedOption)) {
37014                   self.selectedOption = 0;
37015                 } else {
37016                   self.selectedOption = self.selectedOption === elems.length - 1 ?
37017                     self.selectedOption :
37018                     self.selectedOption + 1;
37019                 }
37020                 break;
37021               }
37022               case 38: {
37023                 if (!angular.isNumber(self.selectedOption)) {
37024                   self.selectedOption = elems.length - 1;
37025                 } else {
37026                   self.selectedOption = self.selectedOption === 0 ?
37027                     0 : self.selectedOption - 1;
37028                 }
37029                 break;
37030               }
37031             }
37032             elems[self.selectedOption].focus();
37033           };
37034
37035           scope.getDropdownElement = function() {
37036             return self.dropdownMenu;
37037           };
37038
37039           scope.focusToggleElement = function() {
37040             if (self.toggleElement) {
37041               self.toggleElement[0].focus();
37042             }
37043           };
37044
37045           scope.$watch('isOpen', function(isOpen, wasOpen) {
37046             if (appendTo && self.dropdownMenu) {
37047               var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
37048                 css,
37049                 rightalign;
37050
37051               css = {
37052                 top: pos.top + 'px',
37053                 display: isOpen ? 'block' : 'none'
37054               };
37055
37056               rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
37057               if (!rightalign) {
37058                 css.left = pos.left + 'px';
37059                 css.right = 'auto';
37060               } else {
37061                 css.left = 'auto';
37062                 css.right = window.innerWidth -
37063                   (pos.left + $element.prop('offsetWidth')) + 'px';
37064               }
37065
37066               // Need to adjust our positioning to be relative to the appendTo container
37067               // if it's not the body element
37068               if (!appendToBody) {
37069                 var appendOffset = $position.offset(appendTo);
37070
37071                 css.top = pos.top - appendOffset.top + 'px';
37072
37073                 if (!rightalign) {
37074                   css.left = pos.left - appendOffset.left + 'px';
37075                 } else {
37076                   css.right = window.innerWidth -
37077                     (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
37078                 }
37079               }
37080
37081               self.dropdownMenu.css(css);
37082             }
37083
37084             var openContainer = appendTo ? appendTo : $element;
37085
37086             $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
37087               if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
37088                 toggleInvoker($scope, { open: !!isOpen });
37089               }
37090             });
37091
37092             if (isOpen) {
37093               if (self.dropdownMenuTemplateUrl) {
37094                 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
37095                   templateScope = scope.$new();
37096                   $compile(tplContent.trim())(templateScope, function(dropdownElement) {
37097                     var newEl = dropdownElement;
37098                     self.dropdownMenu.replaceWith(newEl);
37099                     self.dropdownMenu = newEl;
37100                   });
37101                 });
37102               }
37103
37104               scope.focusToggleElement();
37105               uibDropdownService.open(scope);
37106             } else {
37107               if (self.dropdownMenuTemplateUrl) {
37108                 if (templateScope) {
37109                   templateScope.$destroy();
37110                 }
37111                 var newEl = angular.element('<ul class="dropdown-menu"></ul>');
37112                 self.dropdownMenu.replaceWith(newEl);
37113                 self.dropdownMenu = newEl;
37114               }
37115
37116               uibDropdownService.close(scope);
37117               self.selectedOption = null;
37118             }
37119
37120             if (angular.isFunction(setIsOpen)) {
37121               setIsOpen($scope, isOpen);
37122             }
37123           });
37124
37125           $scope.$on('$locationChangeSuccess', function() {
37126             if (scope.getAutoClose() !== 'disabled') {
37127               scope.isOpen = false;
37128             }
37129           });
37130         }])
37131
37132         .directive('uibDropdown', function() {
37133           return {
37134             controller: 'UibDropdownController',
37135             link: function(scope, element, attrs, dropdownCtrl) {
37136               dropdownCtrl.init();
37137             }
37138           };
37139         })
37140
37141         .directive('uibDropdownMenu', function() {
37142           return {
37143             restrict: 'A',
37144             require: '?^uibDropdown',
37145             link: function(scope, element, attrs, dropdownCtrl) {
37146               if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
37147                 return;
37148               }
37149
37150               element.addClass('dropdown-menu');
37151
37152               var tplUrl = attrs.templateUrl;
37153               if (tplUrl) {
37154                 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
37155               }
37156
37157               if (!dropdownCtrl.dropdownMenu) {
37158                 dropdownCtrl.dropdownMenu = element;
37159               }
37160             }
37161           };
37162         })
37163
37164         .directive('uibDropdownToggle', function() {
37165           return {
37166             require: '?^uibDropdown',
37167             link: function(scope, element, attrs, dropdownCtrl) {
37168               if (!dropdownCtrl) {
37169                 return;
37170               }
37171
37172               element.addClass('dropdown-toggle');
37173
37174               dropdownCtrl.toggleElement = element;
37175
37176               var toggleDropdown = function(event) {
37177                 event.preventDefault();
37178
37179                 if (!element.hasClass('disabled') && !attrs.disabled) {
37180                   scope.$apply(function() {
37181                     dropdownCtrl.toggle();
37182                   });
37183                 }
37184               };
37185
37186               element.bind('click', toggleDropdown);
37187
37188               // WAI-ARIA
37189               element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
37190               scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
37191                 element.attr('aria-expanded', !!isOpen);
37192               });
37193
37194               scope.$on('$destroy', function() {
37195                 element.unbind('click', toggleDropdown);
37196               });
37197             }
37198           };
37199         });
37200
37201         angular.module('ui.bootstrap.stackedMap', [])
37202         /**
37203          * A helper, internal data structure that acts as a map but also allows getting / removing
37204          * elements in the LIFO order
37205          */
37206           .factory('$$stackedMap', function() {
37207             return {
37208               createNew: function() {
37209                 var stack = [];
37210
37211                 return {
37212                   add: function(key, value) {
37213                     stack.push({
37214                       key: key,
37215                       value: value
37216                     });
37217                   },
37218                   get: function(key) {
37219                     for (var i = 0; i < stack.length; i++) {
37220                       if (key === stack[i].key) {
37221                         return stack[i];
37222                       }
37223                     }
37224                   },
37225                   keys: function() {
37226                     var keys = [];
37227                     for (var i = 0; i < stack.length; i++) {
37228                       keys.push(stack[i].key);
37229                     }
37230                     return keys;
37231                   },
37232                   top: function() {
37233                     return stack[stack.length - 1];
37234                   },
37235                   remove: function(key) {
37236                     var idx = -1;
37237                     for (var i = 0; i < stack.length; i++) {
37238                       if (key === stack[i].key) {
37239                         idx = i;
37240                         break;
37241                       }
37242                     }
37243                     return stack.splice(idx, 1)[0];
37244                   },
37245                   removeTop: function() {
37246                     return stack.splice(stack.length - 1, 1)[0];
37247                   },
37248                   length: function() {
37249                     return stack.length;
37250                   }
37251                 };
37252               }
37253             };
37254           });
37255         angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
37256         /**
37257          * A helper, internal data structure that stores all references attached to key
37258          */
37259           .factory('$$multiMap', function() {
37260             return {
37261               createNew: function() {
37262                 var map = {};
37263
37264                 return {
37265                   entries: function() {
37266                     return Object.keys(map).map(function(key) {
37267                       return {
37268                         key: key,
37269                         value: map[key]
37270                       };
37271                     });
37272                   },
37273                   get: function(key) {
37274                     return map[key];
37275                   },
37276                   hasKey: function(key) {
37277                     return !!map[key];
37278                   },
37279                   keys: function() {
37280                     return Object.keys(map);
37281                   },
37282                   put: function(key, value) {
37283                     if (!map[key]) {
37284                       map[key] = [];
37285                     }
37286
37287                     map[key].push(value);
37288                   },
37289                   remove: function(key, value) {
37290                     var values = map[key];
37291
37292                     if (!values) {
37293                       return;
37294                     }
37295
37296                     var idx = values.indexOf(value);
37297
37298                     if (idx !== -1) {
37299                       values.splice(idx, 1);
37300                     }
37301
37302                     if (!values.length) {
37303                       delete map[key];
37304                     }
37305                   }
37306                 };
37307               }
37308             };
37309           })
37310
37311         /**
37312          * Pluggable resolve mechanism for the modal resolve resolution
37313          * Supports UI Router's $resolve service
37314          */
37315           .provider('$uibResolve', function() {
37316             var resolve = this;
37317             this.resolver = null;
37318
37319             this.setResolver = function(resolver) {
37320               this.resolver = resolver;
37321             };
37322
37323             this.$get = ['$injector', '$q', function($injector, $q) {
37324               var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
37325               return {
37326                 resolve: function(invocables, locals, parent, self) {
37327                   if (resolver) {
37328                     return resolver.resolve(invocables, locals, parent, self);
37329                   }
37330
37331                   var promises = [];
37332
37333                   angular.forEach(invocables, function(value) {
37334                     if (angular.isFunction(value) || angular.isArray(value)) {
37335                       promises.push($q.resolve($injector.invoke(value)));
37336                     } else if (angular.isString(value)) {
37337                       promises.push($q.resolve($injector.get(value)));
37338                     } else {
37339                       promises.push($q.resolve(value));
37340                     }
37341                   });
37342
37343                   return $q.all(promises).then(function(resolves) {
37344                     var resolveObj = {};
37345                     var resolveIter = 0;
37346                     angular.forEach(invocables, function(value, key) {
37347                       resolveObj[key] = resolves[resolveIter++];
37348                     });
37349
37350                     return resolveObj;
37351                   });
37352                 }
37353               };
37354             }];
37355           })
37356
37357         /**
37358          * A helper directive for the $modal service. It creates a backdrop element.
37359          */
37360           .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack',
37361           function($animateCss, $injector, $modalStack) {
37362             return {
37363               replace: true,
37364               templateUrl: 'uib/template/modal/backdrop.html',
37365               compile: function(tElement, tAttrs) {
37366                 tElement.addClass(tAttrs.backdropClass);
37367                 return linkFn;
37368               }
37369             };
37370
37371             function linkFn(scope, element, attrs) {
37372               if (attrs.modalInClass) {
37373                 $animateCss(element, {
37374                   addClass: attrs.modalInClass
37375                 }).start();
37376
37377                 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37378                   var done = setIsAsync();
37379                   if (scope.modalOptions.animation) {
37380                     $animateCss(element, {
37381                       removeClass: attrs.modalInClass
37382                     }).start().then(done);
37383                   } else {
37384                     done();
37385                   }
37386                 });
37387               }
37388             }
37389           }])
37390
37391           .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
37392           function($modalStack, $q, $animate, $animateCss, $document) {
37393             return {
37394               scope: {
37395                 index: '@'
37396               },
37397               replace: true,
37398               transclude: true,
37399               templateUrl: function(tElement, tAttrs) {
37400                 return tAttrs.templateUrl || 'uib/template/modal/window.html';
37401               },
37402               link: function(scope, element, attrs) {
37403                 element.addClass(attrs.windowClass || '');
37404                 element.addClass(attrs.windowTopClass || '');
37405                 scope.size = attrs.size;
37406
37407                 scope.close = function(evt) {
37408                   var modal = $modalStack.getTop();
37409                   if (modal && modal.value.backdrop &&
37410                     modal.value.backdrop !== 'static' &&
37411                     evt.target === evt.currentTarget) {
37412                     evt.preventDefault();
37413                     evt.stopPropagation();
37414                     $modalStack.dismiss(modal.key, 'backdrop click');
37415                   }
37416                 };
37417
37418                 // moved from template to fix issue #2280
37419                 element.on('click', scope.close);
37420
37421                 // This property is only added to the scope for the purpose of detecting when this directive is rendered.
37422                 // We can detect that by using this property in the template associated with this directive and then use
37423                 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
37424                 scope.$isRendered = true;
37425
37426                 // Deferred object that will be resolved when this modal is render.
37427                 var modalRenderDeferObj = $q.defer();
37428                 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
37429                 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
37430                 attrs.$observe('modalRender', function(value) {
37431                   if (value === 'true') {
37432                     modalRenderDeferObj.resolve();
37433                   }
37434                 });
37435
37436                 modalRenderDeferObj.promise.then(function() {
37437                   var animationPromise = null;
37438
37439                   if (attrs.modalInClass) {
37440                     animationPromise = $animateCss(element, {
37441                       addClass: attrs.modalInClass
37442                     }).start();
37443
37444                     scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37445                       var done = setIsAsync();
37446                       if ($animateCss) {
37447                         $animateCss(element, {
37448                           removeClass: attrs.modalInClass
37449                         }).start().then(done);
37450                       } else {
37451                         $animate.removeClass(element, attrs.modalInClass).then(done);
37452                       }
37453                     });
37454                   }
37455
37456
37457                   $q.when(animationPromise).then(function() {
37458                     /**
37459                      * If something within the freshly-opened modal already has focus (perhaps via a
37460                      * directive that causes focus). then no need to try and focus anything.
37461                      */
37462                     if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
37463                       var inputWithAutofocus = element[0].querySelector('[autofocus]');
37464                       /**
37465                        * Auto-focusing of a freshly-opened modal element causes any child elements
37466                        * with the autofocus attribute to lose focus. This is an issue on touch
37467                        * based devices which will show and then hide the onscreen keyboard.
37468                        * Attempts to refocus the autofocus element via JavaScript will not reopen
37469                        * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
37470                        * the modal element if the modal does not contain an autofocus element.
37471                        */
37472                       if (inputWithAutofocus) {
37473                         inputWithAutofocus.focus();
37474                       } else {
37475                         element[0].focus();
37476                       }
37477                     }
37478                   });
37479
37480                   // Notify {@link $modalStack} that modal is rendered.
37481                   var modal = $modalStack.getTop();
37482                   if (modal) {
37483                     $modalStack.modalRendered(modal.key);
37484                   }
37485                 });
37486               }
37487             };
37488           }])
37489
37490           .directive('uibModalAnimationClass', function() {
37491             return {
37492               compile: function(tElement, tAttrs) {
37493                 if (tAttrs.modalAnimation) {
37494                   tElement.addClass(tAttrs.uibModalAnimationClass);
37495                 }
37496               }
37497             };
37498           })
37499
37500           .directive('uibModalTransclude', function() {
37501             return {
37502               link: function(scope, element, attrs, controller, transclude) {
37503                 transclude(scope.$parent, function(clone) {
37504                   element.empty();
37505                   element.append(clone);
37506                 });
37507               }
37508             };
37509           })
37510
37511           .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
37512             '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap',
37513             function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) {
37514               var OPENED_MODAL_CLASS = 'modal-open';
37515
37516               var backdropDomEl, backdropScope;
37517               var openedWindows = $$stackedMap.createNew();
37518               var openedClasses = $$multiMap.createNew();
37519               var $modalStack = {
37520                 NOW_CLOSING_EVENT: 'modal.stack.now-closing'
37521               };
37522
37523               //Modal focus behavior
37524               var focusableElementList;
37525               var focusIndex = 0;
37526               var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
37527                 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
37528                 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
37529
37530               function backdropIndex() {
37531                 var topBackdropIndex = -1;
37532                 var opened = openedWindows.keys();
37533                 for (var i = 0; i < opened.length; i++) {
37534                   if (openedWindows.get(opened[i]).value.backdrop) {
37535                     topBackdropIndex = i;
37536                   }
37537                 }
37538                 return topBackdropIndex;
37539               }
37540
37541               $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
37542                 if (backdropScope) {
37543                   backdropScope.index = newBackdropIndex;
37544                 }
37545               });
37546
37547               function removeModalWindow(modalInstance, elementToReceiveFocus) {
37548                 var modalWindow = openedWindows.get(modalInstance).value;
37549                 var appendToElement = modalWindow.appendTo;
37550
37551                 //clean up the stack
37552                 openedWindows.remove(modalInstance);
37553
37554                 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
37555                   var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
37556                   openedClasses.remove(modalBodyClass, modalInstance);
37557                   appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
37558                   toggleTopWindowClass(true);
37559                 });
37560                 checkRemoveBackdrop();
37561
37562                 //move focus to specified element if available, or else to body
37563                 if (elementToReceiveFocus && elementToReceiveFocus.focus) {
37564                   elementToReceiveFocus.focus();
37565                 } else if (appendToElement.focus) {
37566                   appendToElement.focus();
37567                 }
37568               }
37569
37570               // Add or remove "windowTopClass" from the top window in the stack
37571               function toggleTopWindowClass(toggleSwitch) {
37572                 var modalWindow;
37573
37574                 if (openedWindows.length() > 0) {
37575                   modalWindow = openedWindows.top().value;
37576                   modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
37577                 }
37578               }
37579
37580               function checkRemoveBackdrop() {
37581                 //remove backdrop if no longer needed
37582                 if (backdropDomEl && backdropIndex() === -1) {
37583                   var backdropScopeRef = backdropScope;
37584                   removeAfterAnimate(backdropDomEl, backdropScope, function() {
37585                     backdropScopeRef = null;
37586                   });
37587                   backdropDomEl = undefined;
37588                   backdropScope = undefined;
37589                 }
37590               }
37591
37592               function removeAfterAnimate(domEl, scope, done, closedDeferred) {
37593                 var asyncDeferred;
37594                 var asyncPromise = null;
37595                 var setIsAsync = function() {
37596                   if (!asyncDeferred) {
37597                     asyncDeferred = $q.defer();
37598                     asyncPromise = asyncDeferred.promise;
37599                   }
37600
37601                   return function asyncDone() {
37602                     asyncDeferred.resolve();
37603                   };
37604                 };
37605                 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
37606
37607                 // Note that it's intentional that asyncPromise might be null.
37608                 // That's when setIsAsync has not been called during the
37609                 // NOW_CLOSING_EVENT broadcast.
37610                 return $q.when(asyncPromise).then(afterAnimating);
37611
37612                 function afterAnimating() {
37613                   if (afterAnimating.done) {
37614                     return;
37615                   }
37616                   afterAnimating.done = true;
37617
37618                   $animateCss(domEl, {
37619                     event: 'leave'
37620                   }).start().then(function() {
37621                     domEl.remove();
37622                     if (closedDeferred) {
37623                       closedDeferred.resolve();
37624                     }
37625                   });
37626
37627                   scope.$destroy();
37628                   if (done) {
37629                     done();
37630                   }
37631                 }
37632               }
37633
37634               $document.on('keydown', keydownListener);
37635
37636               $rootScope.$on('$destroy', function() {
37637                 $document.off('keydown', keydownListener);
37638               });
37639
37640               function keydownListener(evt) {
37641                 if (evt.isDefaultPrevented()) {
37642                   return evt;
37643                 }
37644
37645                 var modal = openedWindows.top();
37646                 if (modal) {
37647                   switch (evt.which) {
37648                     case 27: {
37649                       if (modal.value.keyboard) {
37650                         evt.preventDefault();
37651                         $rootScope.$apply(function() {
37652                           $modalStack.dismiss(modal.key, 'escape key press');
37653                         });
37654                       }
37655                       break;
37656                     }
37657                     case 9: {
37658                       $modalStack.loadFocusElementList(modal);
37659                       var focusChanged = false;
37660                       if (evt.shiftKey) {
37661                         if ($modalStack.isFocusInFirstItem(evt)) {
37662                           focusChanged = $modalStack.focusLastFocusableElement();
37663                         }
37664                       } else {
37665                         if ($modalStack.isFocusInLastItem(evt)) {
37666                           focusChanged = $modalStack.focusFirstFocusableElement();
37667                         }
37668                       }
37669
37670                       if (focusChanged) {
37671                         evt.preventDefault();
37672                         evt.stopPropagation();
37673                       }
37674                       break;
37675                     }
37676                   }
37677                 }
37678               }
37679
37680               $modalStack.open = function(modalInstance, modal) {
37681                 var modalOpener = $document[0].activeElement,
37682                   modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
37683
37684                 toggleTopWindowClass(false);
37685
37686                 openedWindows.add(modalInstance, {
37687                   deferred: modal.deferred,
37688                   renderDeferred: modal.renderDeferred,
37689                   closedDeferred: modal.closedDeferred,
37690                   modalScope: modal.scope,
37691                   backdrop: modal.backdrop,
37692                   keyboard: modal.keyboard,
37693                   openedClass: modal.openedClass,
37694                   windowTopClass: modal.windowTopClass,
37695                   animation: modal.animation,
37696                   appendTo: modal.appendTo
37697                 });
37698
37699                 openedClasses.put(modalBodyClass, modalInstance);
37700
37701                 var appendToElement = modal.appendTo,
37702                     currBackdropIndex = backdropIndex();
37703
37704                 if (!appendToElement.length) {
37705                   throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
37706                 }
37707
37708                 if (currBackdropIndex >= 0 && !backdropDomEl) {
37709                   backdropScope = $rootScope.$new(true);
37710                   backdropScope.modalOptions = modal;
37711                   backdropScope.index = currBackdropIndex;
37712                   backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
37713                   backdropDomEl.attr('backdrop-class', modal.backdropClass);
37714                   if (modal.animation) {
37715                     backdropDomEl.attr('modal-animation', 'true');
37716                   }
37717                   $compile(backdropDomEl)(backdropScope);
37718                   $animate.enter(backdropDomEl, appendToElement);
37719                 }
37720
37721                 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
37722                 angularDomEl.attr({
37723                   'template-url': modal.windowTemplateUrl,
37724                   'window-class': modal.windowClass,
37725                   'window-top-class': modal.windowTopClass,
37726                   'size': modal.size,
37727                   'index': openedWindows.length() - 1,
37728                   'animate': 'animate'
37729                 }).html(modal.content);
37730                 if (modal.animation) {
37731                   angularDomEl.attr('modal-animation', 'true');
37732                 }
37733
37734                 $animate.enter(angularDomEl, appendToElement)
37735                   .then(function() {
37736                     $compile(angularDomEl)(modal.scope);
37737                     $animate.addClass(appendToElement, modalBodyClass);
37738                   });
37739
37740                 openedWindows.top().value.modalDomEl = angularDomEl;
37741                 openedWindows.top().value.modalOpener = modalOpener;
37742
37743                 $modalStack.clearFocusListCache();
37744               };
37745
37746               function broadcastClosing(modalWindow, resultOrReason, closing) {
37747                 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
37748               }
37749
37750               $modalStack.close = function(modalInstance, result) {
37751                 var modalWindow = openedWindows.get(modalInstance);
37752                 if (modalWindow && broadcastClosing(modalWindow, result, true)) {
37753                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37754                   modalWindow.value.deferred.resolve(result);
37755                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37756                   return true;
37757                 }
37758                 return !modalWindow;
37759               };
37760
37761               $modalStack.dismiss = function(modalInstance, reason) {
37762                 var modalWindow = openedWindows.get(modalInstance);
37763                 if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
37764                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37765                   modalWindow.value.deferred.reject(reason);
37766                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37767                   return true;
37768                 }
37769                 return !modalWindow;
37770               };
37771
37772               $modalStack.dismissAll = function(reason) {
37773                 var topModal = this.getTop();
37774                 while (topModal && this.dismiss(topModal.key, reason)) {
37775                   topModal = this.getTop();
37776                 }
37777               };
37778
37779               $modalStack.getTop = function() {
37780                 return openedWindows.top();
37781               };
37782
37783               $modalStack.modalRendered = function(modalInstance) {
37784                 var modalWindow = openedWindows.get(modalInstance);
37785                 if (modalWindow) {
37786                   modalWindow.value.renderDeferred.resolve();
37787                 }
37788               };
37789
37790               $modalStack.focusFirstFocusableElement = function() {
37791                 if (focusableElementList.length > 0) {
37792                   focusableElementList[0].focus();
37793                   return true;
37794                 }
37795                 return false;
37796               };
37797               $modalStack.focusLastFocusableElement = function() {
37798                 if (focusableElementList.length > 0) {
37799                   focusableElementList[focusableElementList.length - 1].focus();
37800                   return true;
37801                 }
37802                 return false;
37803               };
37804
37805               $modalStack.isFocusInFirstItem = function(evt) {
37806                 if (focusableElementList.length > 0) {
37807                   return (evt.target || evt.srcElement) === focusableElementList[0];
37808                 }
37809                 return false;
37810               };
37811
37812               $modalStack.isFocusInLastItem = function(evt) {
37813                 if (focusableElementList.length > 0) {
37814                   return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
37815                 }
37816                 return false;
37817               };
37818
37819               $modalStack.clearFocusListCache = function() {
37820                 focusableElementList = [];
37821                 focusIndex = 0;
37822               };
37823
37824               $modalStack.loadFocusElementList = function(modalWindow) {
37825                 if (focusableElementList === undefined || !focusableElementList.length) {
37826                   if (modalWindow) {
37827                     var modalDomE1 = modalWindow.value.modalDomEl;
37828                     if (modalDomE1 && modalDomE1.length) {
37829                       focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
37830                     }
37831                   }
37832                 }
37833               };
37834
37835               return $modalStack;
37836             }])
37837
37838           .provider('$uibModal', function() {
37839             var $modalProvider = {
37840               options: {
37841                 animation: true,
37842                 backdrop: true, //can also be false or 'static'
37843                 keyboard: true
37844               },
37845               $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
37846                 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
37847                   var $modal = {};
37848
37849                   function getTemplatePromise(options) {
37850                     return options.template ? $q.when(options.template) :
37851                       $templateRequest(angular.isFunction(options.templateUrl) ?
37852                         options.templateUrl() : options.templateUrl);
37853                   }
37854
37855                   var promiseChain = null;
37856                   $modal.getPromiseChain = function() {
37857                     return promiseChain;
37858                   };
37859
37860                   $modal.open = function(modalOptions) {
37861                     var modalResultDeferred = $q.defer();
37862                     var modalOpenedDeferred = $q.defer();
37863                     var modalClosedDeferred = $q.defer();
37864                     var modalRenderDeferred = $q.defer();
37865
37866                     //prepare an instance of a modal to be injected into controllers and returned to a caller
37867                     var modalInstance = {
37868                       result: modalResultDeferred.promise,
37869                       opened: modalOpenedDeferred.promise,
37870                       closed: modalClosedDeferred.promise,
37871                       rendered: modalRenderDeferred.promise,
37872                       close: function (result) {
37873                         return $modalStack.close(modalInstance, result);
37874                       },
37875                       dismiss: function (reason) {
37876                         return $modalStack.dismiss(modalInstance, reason);
37877                       }
37878                     };
37879
37880                     //merge and clean up options
37881                     modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
37882                     modalOptions.resolve = modalOptions.resolve || {};
37883                     modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
37884
37885                     //verify options
37886                     if (!modalOptions.template && !modalOptions.templateUrl) {
37887                       throw new Error('One of template or templateUrl options is required.');
37888                     }
37889
37890                     var templateAndResolvePromise =
37891                       $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
37892
37893                     function resolveWithTemplate() {
37894                       return templateAndResolvePromise;
37895                     }
37896
37897                     // Wait for the resolution of the existing promise chain.
37898                     // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
37899                     // Then add to $modalStack and resolve opened.
37900                     // Finally clean up the chain variable if no subsequent modal has overwritten it.
37901                     var samePromise;
37902                     samePromise = promiseChain = $q.all([promiseChain])
37903                       .then(resolveWithTemplate, resolveWithTemplate)
37904                       .then(function resolveSuccess(tplAndVars) {
37905                         var providedScope = modalOptions.scope || $rootScope;
37906
37907                         var modalScope = providedScope.$new();
37908                         modalScope.$close = modalInstance.close;
37909                         modalScope.$dismiss = modalInstance.dismiss;
37910
37911                         modalScope.$on('$destroy', function() {
37912                           if (!modalScope.$$uibDestructionScheduled) {
37913                             modalScope.$dismiss('$uibUnscheduledDestruction');
37914                           }
37915                         });
37916
37917                         var ctrlInstance, ctrlLocals = {};
37918
37919                         //controllers
37920                         if (modalOptions.controller) {
37921                           ctrlLocals.$scope = modalScope;
37922                           ctrlLocals.$uibModalInstance = modalInstance;
37923                           angular.forEach(tplAndVars[1], function(value, key) {
37924                             ctrlLocals[key] = value;
37925                           });
37926
37927                           ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
37928                           if (modalOptions.controllerAs) {
37929                             if (modalOptions.bindToController) {
37930                               ctrlInstance.$close = modalScope.$close;
37931                               ctrlInstance.$dismiss = modalScope.$dismiss;
37932                               angular.extend(ctrlInstance, providedScope);
37933                             }
37934
37935                             modalScope[modalOptions.controllerAs] = ctrlInstance;
37936                           }
37937                         }
37938
37939                         $modalStack.open(modalInstance, {
37940                           scope: modalScope,
37941                           deferred: modalResultDeferred,
37942                           renderDeferred: modalRenderDeferred,
37943                           closedDeferred: modalClosedDeferred,
37944                           content: tplAndVars[0],
37945                           animation: modalOptions.animation,
37946                           backdrop: modalOptions.backdrop,
37947                           keyboard: modalOptions.keyboard,
37948                           backdropClass: modalOptions.backdropClass,
37949                           windowTopClass: modalOptions.windowTopClass,
37950                           windowClass: modalOptions.windowClass,
37951                           windowTemplateUrl: modalOptions.windowTemplateUrl,
37952                           size: modalOptions.size,
37953                           openedClass: modalOptions.openedClass,
37954                           appendTo: modalOptions.appendTo
37955                         });
37956                         modalOpenedDeferred.resolve(true);
37957
37958                     }, function resolveError(reason) {
37959                       modalOpenedDeferred.reject(reason);
37960                       modalResultDeferred.reject(reason);
37961                     })['finally'](function() {
37962                       if (promiseChain === samePromise) {
37963                         promiseChain = null;
37964                       }
37965                     });
37966
37967                     return modalInstance;
37968                   };
37969
37970                   return $modal;
37971                 }
37972               ]
37973             };
37974
37975             return $modalProvider;
37976           });
37977
37978         angular.module('ui.bootstrap.paging', [])
37979         /**
37980          * Helper internal service for generating common controller code between the
37981          * pager and pagination components
37982          */
37983         .factory('uibPaging', ['$parse', function($parse) {
37984           return {
37985             create: function(ctrl, $scope, $attrs) {
37986               ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
37987               ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
37988
37989               ctrl.init = function(ngModelCtrl, config) {
37990                 ctrl.ngModelCtrl = ngModelCtrl;
37991                 ctrl.config = config;
37992
37993                 ngModelCtrl.$render = function() {
37994                   ctrl.render();
37995                 };
37996
37997                 if ($attrs.itemsPerPage) {
37998                   $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
37999                     ctrl.itemsPerPage = parseInt(value, 10);
38000                     $scope.totalPages = ctrl.calculateTotalPages();
38001                     ctrl.updatePage();
38002                   });
38003                 } else {
38004                   ctrl.itemsPerPage = config.itemsPerPage;
38005                 }
38006
38007                 $scope.$watch('totalItems', function(newTotal, oldTotal) {
38008                   if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
38009                     $scope.totalPages = ctrl.calculateTotalPages();
38010                     ctrl.updatePage();
38011                   }
38012                 });
38013               };
38014
38015               ctrl.calculateTotalPages = function() {
38016                 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
38017                 return Math.max(totalPages || 0, 1);
38018               };
38019
38020               ctrl.render = function() {
38021                 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
38022               };
38023
38024               $scope.selectPage = function(page, evt) {
38025                 if (evt) {
38026                   evt.preventDefault();
38027                 }
38028
38029                 var clickAllowed = !$scope.ngDisabled || !evt;
38030                 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
38031                   if (evt && evt.target) {
38032                     evt.target.blur();
38033                   }
38034                   ctrl.ngModelCtrl.$setViewValue(page);
38035                   ctrl.ngModelCtrl.$render();
38036                 }
38037               };
38038
38039               $scope.getText = function(key) {
38040                 return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
38041               };
38042
38043               $scope.noPrevious = function() {
38044                 return $scope.page === 1;
38045               };
38046
38047               $scope.noNext = function() {
38048                 return $scope.page === $scope.totalPages;
38049               };
38050
38051               ctrl.updatePage = function() {
38052                 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
38053
38054                 if ($scope.page > $scope.totalPages) {
38055                   $scope.selectPage($scope.totalPages);
38056                 } else {
38057                   ctrl.ngModelCtrl.$render();
38058                 }
38059               };
38060             }
38061           };
38062         }]);
38063
38064         angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
38065
38066         .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
38067           $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
38068
38069           uibPaging.create(this, $scope, $attrs);
38070         }])
38071
38072         .constant('uibPagerConfig', {
38073           itemsPerPage: 10,
38074           previousText: '« Previous',
38075           nextText: 'Next »',
38076           align: true
38077         })
38078
38079         .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
38080           return {
38081             scope: {
38082               totalItems: '=',
38083               previousText: '@',
38084               nextText: '@',
38085               ngDisabled: '='
38086             },
38087             require: ['uibPager', '?ngModel'],
38088             controller: 'UibPagerController',
38089             controllerAs: 'pager',
38090             templateUrl: function(element, attrs) {
38091               return attrs.templateUrl || 'uib/template/pager/pager.html';
38092             },
38093             replace: true,
38094             link: function(scope, element, attrs, ctrls) {
38095               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38096
38097               if (!ngModelCtrl) {
38098                 return; // do nothing if no ng-model
38099               }
38100
38101               paginationCtrl.init(ngModelCtrl, uibPagerConfig);
38102             }
38103           };
38104         }]);
38105
38106         angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
38107         .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
38108           var ctrl = this;
38109           // Setup configuration parameters
38110           var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
38111             rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
38112             forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
38113             boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
38114           $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
38115           $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
38116
38117           uibPaging.create(this, $scope, $attrs);
38118
38119           if ($attrs.maxSize) {
38120             $scope.$parent.$watch($parse($attrs.maxSize), function(value) {
38121               maxSize = parseInt(value, 10);
38122               ctrl.render();
38123             });
38124           }
38125
38126           // Create page object used in template
38127           function makePage(number, text, isActive) {
38128             return {
38129               number: number,
38130               text: text,
38131               active: isActive
38132             };
38133           }
38134
38135           function getPages(currentPage, totalPages) {
38136             var pages = [];
38137
38138             // Default page limits
38139             var startPage = 1, endPage = totalPages;
38140             var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
38141
38142             // recompute if maxSize
38143             if (isMaxSized) {
38144               if (rotate) {
38145                 // Current page is displayed in the middle of the visible ones
38146                 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
38147                 endPage = startPage + maxSize - 1;
38148
38149                 // Adjust if limit is exceeded
38150                 if (endPage > totalPages) {
38151                   endPage = totalPages;
38152                   startPage = endPage - maxSize + 1;
38153                 }
38154               } else {
38155                 // Visible pages are paginated with maxSize
38156                 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
38157
38158                 // Adjust last page if limit is exceeded
38159                 endPage = Math.min(startPage + maxSize - 1, totalPages);
38160               }
38161             }
38162
38163             // Add page number links
38164             for (var number = startPage; number <= endPage; number++) {
38165               var page = makePage(number, number, number === currentPage);
38166               pages.push(page);
38167             }
38168
38169             // Add links to move between page sets
38170             if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
38171               if (startPage > 1) {
38172                 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
38173                 var previousPageSet = makePage(startPage - 1, '...', false);
38174                 pages.unshift(previousPageSet);
38175               }
38176                 if (boundaryLinkNumbers) {
38177                   if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
38178                     var secondPageLink = makePage(2, '2', false);
38179                     pages.unshift(secondPageLink);
38180                   }
38181                   //add the first page
38182                   var firstPageLink = makePage(1, '1', false);
38183                   pages.unshift(firstPageLink);
38184                 }
38185               }
38186
38187               if (endPage < totalPages) {
38188                 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
38189                 var nextPageSet = makePage(endPage + 1, '...', false);
38190                 pages.push(nextPageSet);
38191               }
38192                 if (boundaryLinkNumbers) {
38193                   if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
38194                     var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
38195                     pages.push(secondToLastPageLink);
38196                   }
38197                   //add the last page
38198                   var lastPageLink = makePage(totalPages, totalPages, false);
38199                   pages.push(lastPageLink);
38200                 }
38201               }
38202             }
38203             return pages;
38204           }
38205
38206           var originalRender = this.render;
38207           this.render = function() {
38208             originalRender();
38209             if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
38210               $scope.pages = getPages($scope.page, $scope.totalPages);
38211             }
38212           };
38213         }])
38214
38215         .constant('uibPaginationConfig', {
38216           itemsPerPage: 10,
38217           boundaryLinks: false,
38218           boundaryLinkNumbers: false,
38219           directionLinks: true,
38220           firstText: 'First',
38221           previousText: 'Previous',
38222           nextText: 'Next',
38223           lastText: 'Last',
38224           rotate: true,
38225           forceEllipses: false
38226         })
38227
38228         .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
38229           return {
38230             scope: {
38231               totalItems: '=',
38232               firstText: '@',
38233               previousText: '@',
38234               nextText: '@',
38235               lastText: '@',
38236               ngDisabled:'='
38237             },
38238             require: ['uibPagination', '?ngModel'],
38239             controller: 'UibPaginationController',
38240             controllerAs: 'pagination',
38241             templateUrl: function(element, attrs) {
38242               return attrs.templateUrl || 'uib/template/pagination/pagination.html';
38243             },
38244             replace: true,
38245             link: function(scope, element, attrs, ctrls) {
38246               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38247
38248               if (!ngModelCtrl) {
38249                  return; // do nothing if no ng-model
38250               }
38251
38252               paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
38253             }
38254           };
38255         }]);
38256
38257         /**
38258          * The following features are still outstanding: animation as a
38259          * function, placement as a function, inside, support for more triggers than
38260          * just mouse enter/leave, html tooltips, and selector delegation.
38261          */
38262         angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
38263
38264         /**
38265          * The $tooltip service creates tooltip- and popover-like directives as well as
38266          * houses global options for them.
38267          */
38268         .provider('$uibTooltip', function() {
38269           // The default options tooltip and popover.
38270           var defaultOptions = {
38271             placement: 'top',
38272             placementClassPrefix: '',
38273             animation: true,
38274             popupDelay: 0,
38275             popupCloseDelay: 0,
38276             useContentExp: false
38277           };
38278
38279           // Default hide triggers for each show trigger
38280           var triggerMap = {
38281             'mouseenter': 'mouseleave',
38282             'click': 'click',
38283             'outsideClick': 'outsideClick',
38284             'focus': 'blur',
38285             'none': ''
38286           };
38287
38288           // The options specified to the provider globally.
38289           var globalOptions = {};
38290
38291           /**
38292            * `options({})` allows global configuration of all tooltips in the
38293            * application.
38294            *
38295            *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
38296            *     // place tooltips left instead of top by default
38297            *     $tooltipProvider.options( { placement: 'left' } );
38298            *   });
38299            */
38300                 this.options = function(value) {
38301                         angular.extend(globalOptions, value);
38302                 };
38303
38304           /**
38305            * This allows you to extend the set of trigger mappings available. E.g.:
38306            *
38307            *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
38308            */
38309           this.setTriggers = function setTriggers(triggers) {
38310             angular.extend(triggerMap, triggers);
38311           };
38312
38313           /**
38314            * This is a helper function for translating camel-case to snake_case.
38315            */
38316           function snake_case(name) {
38317             var regexp = /[A-Z]/g;
38318             var separator = '-';
38319             return name.replace(regexp, function(letter, pos) {
38320               return (pos ? separator : '') + letter.toLowerCase();
38321             });
38322           }
38323
38324           /**
38325            * Returns the actual instance of the $tooltip service.
38326            * TODO support multiple triggers
38327            */
38328           this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
38329             var openedTooltips = $$stackedMap.createNew();
38330             $document.on('keypress', keypressListener);
38331
38332             $rootScope.$on('$destroy', function() {
38333               $document.off('keypress', keypressListener);
38334             });
38335
38336             function keypressListener(e) {
38337               if (e.which === 27) {
38338                 var last = openedTooltips.top();
38339                 if (last) {
38340                   last.value.close();
38341                   openedTooltips.removeTop();
38342                   last = null;
38343                 }
38344               }
38345             }
38346
38347             return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
38348               options = angular.extend({}, defaultOptions, globalOptions, options);
38349
38350               /**
38351                * Returns an object of show and hide triggers.
38352                *
38353                * If a trigger is supplied,
38354                * it is used to show the tooltip; otherwise, it will use the `trigger`
38355                * option passed to the `$tooltipProvider.options` method; else it will
38356                * default to the trigger supplied to this directive factory.
38357                *
38358                * The hide trigger is based on the show trigger. If the `trigger` option
38359                * was passed to the `$tooltipProvider.options` method, it will use the
38360                * mapped trigger from `triggerMap` or the passed trigger if the map is
38361                * undefined; otherwise, it uses the `triggerMap` value of the show
38362                * trigger; else it will just use the show trigger.
38363                */
38364               function getTriggers(trigger) {
38365                 var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
38366                 var hide = show.map(function(trigger) {
38367                   return triggerMap[trigger] || trigger;
38368                 });
38369                 return {
38370                   show: show,
38371                   hide: hide
38372                 };
38373               }
38374
38375               var directiveName = snake_case(ttType);
38376
38377               var startSym = $interpolate.startSymbol();
38378               var endSym = $interpolate.endSymbol();
38379               var template =
38380                 '<div '+ directiveName + '-popup '+
38381                   'title="' + startSym + 'title' + endSym + '" '+
38382                   (options.useContentExp ?
38383                     'content-exp="contentExp()" ' :
38384                     'content="' + startSym + 'content' + endSym + '" ') +
38385                   'placement="' + startSym + 'placement' + endSym + '" '+
38386                   'popup-class="' + startSym + 'popupClass' + endSym + '" '+
38387                   'animation="animation" ' +
38388                   'is-open="isOpen"' +
38389                   'origin-scope="origScope" ' +
38390                   'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
38391                   '>' +
38392                 '</div>';
38393
38394               return {
38395                 compile: function(tElem, tAttrs) {
38396                   var tooltipLinker = $compile(template);
38397
38398                   return function link(scope, element, attrs, tooltipCtrl) {
38399                     var tooltip;
38400                     var tooltipLinkedScope;
38401                     var transitionTimeout;
38402                     var showTimeout;
38403                     var hideTimeout;
38404                     var positionTimeout;
38405                     var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
38406                     var triggers = getTriggers(undefined);
38407                     var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
38408                     var ttScope = scope.$new(true);
38409                     var repositionScheduled = false;
38410                     var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
38411                     var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
38412                     var observers = [];
38413
38414                     var positionTooltip = function() {
38415                       // check if tooltip exists and is not empty
38416                       if (!tooltip || !tooltip.html()) { return; }
38417
38418                       if (!positionTimeout) {
38419                         positionTimeout = $timeout(function() {
38420                           // Reset the positioning.
38421                           tooltip.css({ top: 0, left: 0 });
38422
38423                           // Now set the calculated positioning.
38424                           var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
38425                           tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' });
38426
38427                           // If the placement class is prefixed, still need
38428                           // to remove the TWBS standard class.
38429                           if (options.placementClassPrefix) {
38430                             tooltip.removeClass('top bottom left right');
38431                           }
38432
38433                           tooltip.removeClass(
38434                             options.placementClassPrefix + 'top ' +
38435                             options.placementClassPrefix + 'top-left ' +
38436                             options.placementClassPrefix + 'top-right ' +
38437                             options.placementClassPrefix + 'bottom ' +
38438                             options.placementClassPrefix + 'bottom-left ' +
38439                             options.placementClassPrefix + 'bottom-right ' +
38440                             options.placementClassPrefix + 'left ' +
38441                             options.placementClassPrefix + 'left-top ' +
38442                             options.placementClassPrefix + 'left-bottom ' +
38443                             options.placementClassPrefix + 'right ' +
38444                             options.placementClassPrefix + 'right-top ' +
38445                             options.placementClassPrefix + 'right-bottom');
38446
38447                           var placement = ttPosition.placement.split('-');
38448                           tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement);
38449                           $position.positionArrow(tooltip, ttPosition.placement);
38450
38451                           positionTimeout = null;
38452                         }, 0, false);
38453                       }
38454                     };
38455
38456                     // Set up the correct scope to allow transclusion later
38457                     ttScope.origScope = scope;
38458
38459                     // By default, the tooltip is not open.
38460                     // TODO add ability to start tooltip opened
38461                     ttScope.isOpen = false;
38462                     openedTooltips.add(ttScope, {
38463                       close: hide
38464                     });
38465
38466                     function toggleTooltipBind() {
38467                       if (!ttScope.isOpen) {
38468                         showTooltipBind();
38469                       } else {
38470                         hideTooltipBind();
38471                       }
38472                     }
38473
38474                     // Show the tooltip with delay if specified, otherwise show it immediately
38475                     function showTooltipBind() {
38476                       if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
38477                         return;
38478                       }
38479
38480                       cancelHide();
38481                       prepareTooltip();
38482
38483                       if (ttScope.popupDelay) {
38484                         // Do nothing if the tooltip was already scheduled to pop-up.
38485                         // This happens if show is triggered multiple times before any hide is triggered.
38486                         if (!showTimeout) {
38487                           showTimeout = $timeout(show, ttScope.popupDelay, false);
38488                         }
38489                       } else {
38490                         show();
38491                       }
38492                     }
38493
38494                     function hideTooltipBind() {
38495                       cancelShow();
38496
38497                       if (ttScope.popupCloseDelay) {
38498                         if (!hideTimeout) {
38499                           hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
38500                         }
38501                       } else {
38502                         hide();
38503                       }
38504                     }
38505
38506                     // Show the tooltip popup element.
38507                     function show() {
38508                       cancelShow();
38509                       cancelHide();
38510
38511                       // Don't show empty tooltips.
38512                       if (!ttScope.content) {
38513                         return angular.noop;
38514                       }
38515
38516                       createTooltip();
38517
38518                       // And show the tooltip.
38519                       ttScope.$evalAsync(function() {
38520                         ttScope.isOpen = true;
38521                         assignIsOpen(true);
38522                         positionTooltip();
38523                       });
38524                     }
38525
38526                     function cancelShow() {
38527                       if (showTimeout) {
38528                         $timeout.cancel(showTimeout);
38529                         showTimeout = null;
38530                       }
38531
38532                       if (positionTimeout) {
38533                         $timeout.cancel(positionTimeout);
38534                         positionTimeout = null;
38535                       }
38536                     }
38537
38538                     // Hide the tooltip popup element.
38539                     function hide() {
38540                       if (!ttScope) {
38541                         return;
38542                       }
38543
38544                       // First things first: we don't show it anymore.
38545                       ttScope.$evalAsync(function() {
38546                         ttScope.isOpen = false;
38547                         assignIsOpen(false);
38548                         // And now we remove it from the DOM. However, if we have animation, we
38549                         // need to wait for it to expire beforehand.
38550                         // FIXME: this is a placeholder for a port of the transitions library.
38551                         // The fade transition in TWBS is 150ms.
38552                         if (ttScope.animation) {
38553                           if (!transitionTimeout) {
38554                             transitionTimeout = $timeout(removeTooltip, 150, false);
38555                           }
38556                         } else {
38557                           removeTooltip();
38558                         }
38559                       });
38560                     }
38561
38562                     function cancelHide() {
38563                       if (hideTimeout) {
38564                         $timeout.cancel(hideTimeout);
38565                         hideTimeout = null;
38566                       }
38567                       if (transitionTimeout) {
38568                         $timeout.cancel(transitionTimeout);
38569                         transitionTimeout = null;
38570                       }
38571                     }
38572
38573                     function createTooltip() {
38574                       // There can only be one tooltip element per directive shown at once.
38575                       if (tooltip) {
38576                         return;
38577                       }
38578
38579                       tooltipLinkedScope = ttScope.$new();
38580                       tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
38581                         if (appendToBody) {
38582                           $document.find('body').append(tooltip);
38583                         } else {
38584                           element.after(tooltip);
38585                         }
38586                       });
38587
38588                       prepObservers();
38589                     }
38590
38591                     function removeTooltip() {
38592                       cancelShow();
38593                       cancelHide();
38594                       unregisterObservers();
38595
38596                       if (tooltip) {
38597                         tooltip.remove();
38598                         tooltip = null;
38599                       }
38600                       if (tooltipLinkedScope) {
38601                         tooltipLinkedScope.$destroy();
38602                         tooltipLinkedScope = null;
38603                       }
38604                     }
38605
38606                     /**
38607                      * Set the initial scope values. Once
38608                      * the tooltip is created, the observers
38609                      * will be added to keep things in sync.
38610                      */
38611                     function prepareTooltip() {
38612                       ttScope.title = attrs[prefix + 'Title'];
38613                       if (contentParse) {
38614                         ttScope.content = contentParse(scope);
38615                       } else {
38616                         ttScope.content = attrs[ttType];
38617                       }
38618
38619                       ttScope.popupClass = attrs[prefix + 'Class'];
38620                       ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
38621
38622                       var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
38623                       var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
38624                       ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
38625                       ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
38626                     }
38627
38628                     function assignIsOpen(isOpen) {
38629                       if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
38630                         isOpenParse.assign(scope, isOpen);
38631                       }
38632                     }
38633
38634                     ttScope.contentExp = function() {
38635                       return ttScope.content;
38636                     };
38637
38638                     /**
38639                      * Observe the relevant attributes.
38640                      */
38641                     attrs.$observe('disabled', function(val) {
38642                       if (val) {
38643                         cancelShow();
38644                       }
38645
38646                       if (val && ttScope.isOpen) {
38647                         hide();
38648                       }
38649                     });
38650
38651                     if (isOpenParse) {
38652                       scope.$watch(isOpenParse, function(val) {
38653                         if (ttScope && !val === ttScope.isOpen) {
38654                           toggleTooltipBind();
38655                         }
38656                       });
38657                     }
38658
38659                     function prepObservers() {
38660                       observers.length = 0;
38661
38662                       if (contentParse) {
38663                         observers.push(
38664                           scope.$watch(contentParse, function(val) {
38665                             ttScope.content = val;
38666                             if (!val && ttScope.isOpen) {
38667                               hide();
38668                             }
38669                           })
38670                         );
38671
38672                         observers.push(
38673                           tooltipLinkedScope.$watch(function() {
38674                             if (!repositionScheduled) {
38675                               repositionScheduled = true;
38676                               tooltipLinkedScope.$$postDigest(function() {
38677                                 repositionScheduled = false;
38678                                 if (ttScope && ttScope.isOpen) {
38679                                   positionTooltip();
38680                                 }
38681                               });
38682                             }
38683                           })
38684                         );
38685                       } else {
38686                         observers.push(
38687                           attrs.$observe(ttType, function(val) {
38688                             ttScope.content = val;
38689                             if (!val && ttScope.isOpen) {
38690                               hide();
38691                             } else {
38692                               positionTooltip();
38693                             }
38694                           })
38695                         );
38696                       }
38697
38698                       observers.push(
38699                         attrs.$observe(prefix + 'Title', function(val) {
38700                           ttScope.title = val;
38701                           if (ttScope.isOpen) {
38702                             positionTooltip();
38703                           }
38704                         })
38705                       );
38706
38707                       observers.push(
38708                         attrs.$observe(prefix + 'Placement', function(val) {
38709                           ttScope.placement = val ? val : options.placement;
38710                           if (ttScope.isOpen) {
38711                             positionTooltip();
38712                           }
38713                         })
38714                       );
38715                     }
38716
38717                     function unregisterObservers() {
38718                       if (observers.length) {
38719                         angular.forEach(observers, function(observer) {
38720                           observer();
38721                         });
38722                         observers.length = 0;
38723                       }
38724                     }
38725
38726                     // hide tooltips/popovers for outsideClick trigger
38727                     function bodyHideTooltipBind(e) {
38728                       if (!ttScope || !ttScope.isOpen || !tooltip) {
38729                         return;
38730                       }
38731                       // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
38732                       if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
38733                         hideTooltipBind();
38734                       }
38735                     }
38736
38737                     var unregisterTriggers = function() {
38738                       triggers.show.forEach(function(trigger) {
38739                         if (trigger === 'outsideClick') {
38740                           element.off('click', toggleTooltipBind);
38741                         } else {
38742                           element.off(trigger, showTooltipBind);
38743                           element.off(trigger, toggleTooltipBind);
38744                         }
38745                       });
38746                       triggers.hide.forEach(function(trigger) {
38747                         if (trigger === 'outsideClick') {
38748                           $document.off('click', bodyHideTooltipBind);
38749                         } else {
38750                           element.off(trigger, hideTooltipBind);
38751                         }
38752                       });
38753                     };
38754
38755                     function prepTriggers() {
38756                       var val = attrs[prefix + 'Trigger'];
38757                       unregisterTriggers();
38758
38759                       triggers = getTriggers(val);
38760
38761                       if (triggers.show !== 'none') {
38762                         triggers.show.forEach(function(trigger, idx) {
38763                           if (trigger === 'outsideClick') {
38764                             element.on('click', toggleTooltipBind);
38765                             $document.on('click', bodyHideTooltipBind);
38766                           } else if (trigger === triggers.hide[idx]) {
38767                             element.on(trigger, toggleTooltipBind);
38768                           } else if (trigger) {
38769                             element.on(trigger, showTooltipBind);
38770                             element.on(triggers.hide[idx], hideTooltipBind);
38771                           }
38772
38773                           element.on('keypress', function(e) {
38774                             if (e.which === 27) {
38775                               hideTooltipBind();
38776                             }
38777                           });
38778                         });
38779                       }
38780                     }
38781
38782                     prepTriggers();
38783
38784                     var animation = scope.$eval(attrs[prefix + 'Animation']);
38785                     ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
38786
38787                     var appendToBodyVal;
38788                     var appendKey = prefix + 'AppendToBody';
38789                     if (appendKey in attrs && attrs[appendKey] === undefined) {
38790                       appendToBodyVal = true;
38791                     } else {
38792                       appendToBodyVal = scope.$eval(attrs[appendKey]);
38793                     }
38794
38795                     appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
38796
38797                     // if a tooltip is attached to <body> we need to remove it on
38798                     // location change as its parent scope will probably not be destroyed
38799                     // by the change.
38800                     if (appendToBody) {
38801                       scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
38802                         if (ttScope.isOpen) {
38803                           hide();
38804                         }
38805                       });
38806                     }
38807
38808                     // Make sure tooltip is destroyed and removed.
38809                     scope.$on('$destroy', function onDestroyTooltip() {
38810                       unregisterTriggers();
38811                       removeTooltip();
38812                       openedTooltips.remove(ttScope);
38813                       ttScope = null;
38814                     });
38815                   };
38816                 }
38817               };
38818             };
38819           }];
38820         })
38821
38822         // This is mostly ngInclude code but with a custom scope
38823         .directive('uibTooltipTemplateTransclude', [
38824                  '$animate', '$sce', '$compile', '$templateRequest',
38825         function ($animate, $sce, $compile, $templateRequest) {
38826           return {
38827             link: function(scope, elem, attrs) {
38828               var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
38829
38830               var changeCounter = 0,
38831                 currentScope,
38832                 previousElement,
38833                 currentElement;
38834
38835               var cleanupLastIncludeContent = function() {
38836                 if (previousElement) {
38837                   previousElement.remove();
38838                   previousElement = null;
38839                 }
38840
38841                 if (currentScope) {
38842                   currentScope.$destroy();
38843                   currentScope = null;
38844                 }
38845
38846                 if (currentElement) {
38847                   $animate.leave(currentElement).then(function() {
38848                     previousElement = null;
38849                   });
38850                   previousElement = currentElement;
38851                   currentElement = null;
38852                 }
38853               };
38854
38855               scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
38856                 var thisChangeId = ++changeCounter;
38857
38858                 if (src) {
38859                   //set the 2nd param to true to ignore the template request error so that the inner
38860                   //contents and scope can be cleaned up.
38861                   $templateRequest(src, true).then(function(response) {
38862                     if (thisChangeId !== changeCounter) { return; }
38863                     var newScope = origScope.$new();
38864                     var template = response;
38865
38866                     var clone = $compile(template)(newScope, function(clone) {
38867                       cleanupLastIncludeContent();
38868                       $animate.enter(clone, elem);
38869                     });
38870
38871                     currentScope = newScope;
38872                     currentElement = clone;
38873
38874                     currentScope.$emit('$includeContentLoaded', src);
38875                   }, function() {
38876                     if (thisChangeId === changeCounter) {
38877                       cleanupLastIncludeContent();
38878                       scope.$emit('$includeContentError', src);
38879                     }
38880                   });
38881                   scope.$emit('$includeContentRequested', src);
38882                 } else {
38883                   cleanupLastIncludeContent();
38884                 }
38885               });
38886
38887               scope.$on('$destroy', cleanupLastIncludeContent);
38888             }
38889           };
38890         }])
38891
38892         /**
38893          * Note that it's intentional that these classes are *not* applied through $animate.
38894          * They must not be animated as they're expected to be present on the tooltip on
38895          * initialization.
38896          */
38897         .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
38898           return {
38899             restrict: 'A',
38900             link: function(scope, element, attrs) {
38901               // need to set the primary position so the
38902               // arrow has space during position measure.
38903               // tooltip.positionTooltip()
38904               if (scope.placement) {
38905                 // // There are no top-left etc... classes
38906                 // // in TWBS, so we need the primary position.
38907                 var position = $uibPosition.parsePlacement(scope.placement);
38908                 element.addClass(position[0]);
38909               } else {
38910                 element.addClass('top');
38911               }
38912
38913               if (scope.popupClass) {
38914                 element.addClass(scope.popupClass);
38915               }
38916
38917               if (scope.animation()) {
38918                 element.addClass(attrs.tooltipAnimationClass);
38919               }
38920             }
38921           };
38922         }])
38923
38924         .directive('uibTooltipPopup', function() {
38925           return {
38926             replace: true,
38927             scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38928             templateUrl: 'uib/template/tooltip/tooltip-popup.html'
38929           };
38930         })
38931
38932         .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
38933           return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
38934         }])
38935
38936         .directive('uibTooltipTemplatePopup', function() {
38937           return {
38938             replace: true,
38939             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38940               originScope: '&' },
38941             templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
38942           };
38943         })
38944
38945         .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
38946           return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
38947             useContentExp: true
38948           });
38949         }])
38950
38951         .directive('uibTooltipHtmlPopup', function() {
38952           return {
38953             replace: true,
38954             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38955             templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
38956           };
38957         })
38958
38959         .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
38960           return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
38961             useContentExp: true
38962           });
38963         }]);
38964
38965         /**
38966          * The following features are still outstanding: popup delay, animation as a
38967          * function, placement as a function, inside, support for more triggers than
38968          * just mouse enter/leave, and selector delegatation.
38969          */
38970         angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
38971
38972         .directive('uibPopoverTemplatePopup', function() {
38973           return {
38974             replace: true,
38975             scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38976               originScope: '&' },
38977             templateUrl: 'uib/template/popover/popover-template.html'
38978           };
38979         })
38980
38981         .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
38982           return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
38983             useContentExp: true
38984           });
38985         }])
38986
38987         .directive('uibPopoverHtmlPopup', function() {
38988           return {
38989             replace: true,
38990             scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38991             templateUrl: 'uib/template/popover/popover-html.html'
38992           };
38993         })
38994
38995         .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
38996           return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
38997             useContentExp: true
38998           });
38999         }])
39000
39001         .directive('uibPopoverPopup', function() {
39002           return {
39003             replace: true,
39004             scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
39005             templateUrl: 'uib/template/popover/popover.html'
39006           };
39007         })
39008
39009         .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
39010           return $uibTooltip('uibPopover', 'popover', 'click');
39011         }]);
39012
39013         angular.module('ui.bootstrap.progressbar', [])
39014
39015         .constant('uibProgressConfig', {
39016           animate: true,
39017           max: 100
39018         })
39019
39020         .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
39021           var self = this,
39022               animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
39023
39024           this.bars = [];
39025           $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
39026
39027           this.addBar = function(bar, element, attrs) {
39028             if (!animate) {
39029               element.css({'transition': 'none'});
39030             }
39031
39032             this.bars.push(bar);
39033
39034             bar.max = $scope.max;
39035             bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
39036
39037             bar.$watch('value', function(value) {
39038               bar.recalculatePercentage();
39039             });
39040
39041             bar.recalculatePercentage = function() {
39042               var totalPercentage = self.bars.reduce(function(total, bar) {
39043                 bar.percent = +(100 * bar.value / bar.max).toFixed(2);
39044                 return total + bar.percent;
39045               }, 0);
39046
39047               if (totalPercentage > 100) {
39048                 bar.percent -= totalPercentage - 100;
39049               }
39050             };
39051
39052             bar.$on('$destroy', function() {
39053               element = null;
39054               self.removeBar(bar);
39055             });
39056           };
39057
39058           this.removeBar = function(bar) {
39059             this.bars.splice(this.bars.indexOf(bar), 1);
39060             this.bars.forEach(function (bar) {
39061               bar.recalculatePercentage();
39062             });
39063           };
39064
39065           $scope.$watch('max', function(max) {
39066             self.bars.forEach(function(bar) {
39067               bar.max = $scope.max;
39068               bar.recalculatePercentage();
39069             });
39070           });
39071         }])
39072
39073         .directive('uibProgress', function() {
39074           return {
39075             replace: true,
39076             transclude: true,
39077             controller: 'UibProgressController',
39078             require: 'uibProgress',
39079             scope: {
39080               max: '=?'
39081             },
39082             templateUrl: 'uib/template/progressbar/progress.html'
39083           };
39084         })
39085
39086         .directive('uibBar', function() {
39087           return {
39088             replace: true,
39089             transclude: true,
39090             require: '^uibProgress',
39091             scope: {
39092               value: '=',
39093               type: '@'
39094             },
39095             templateUrl: 'uib/template/progressbar/bar.html',
39096             link: function(scope, element, attrs, progressCtrl) {
39097               progressCtrl.addBar(scope, element, attrs);
39098             }
39099           };
39100         })
39101
39102         .directive('uibProgressbar', function() {
39103           return {
39104             replace: true,
39105             transclude: true,
39106             controller: 'UibProgressController',
39107             scope: {
39108               value: '=',
39109               max: '=?',
39110               type: '@'
39111             },
39112             templateUrl: 'uib/template/progressbar/progressbar.html',
39113             link: function(scope, element, attrs, progressCtrl) {
39114               progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
39115             }
39116           };
39117         });
39118
39119         angular.module('ui.bootstrap.rating', [])
39120
39121         .constant('uibRatingConfig', {
39122           max: 5,
39123           stateOn: null,
39124           stateOff: null,
39125           titles : ['one', 'two', 'three', 'four', 'five']
39126         })
39127
39128         .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
39129           var ngModelCtrl = { $setViewValue: angular.noop };
39130
39131           this.init = function(ngModelCtrl_) {
39132             ngModelCtrl = ngModelCtrl_;
39133             ngModelCtrl.$render = this.render;
39134
39135             ngModelCtrl.$formatters.push(function(value) {
39136               if (angular.isNumber(value) && value << 0 !== value) {
39137                 value = Math.round(value);
39138               }
39139
39140               return value;
39141             });
39142
39143             this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
39144             this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
39145             var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
39146             this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
39147               tmpTitles : ratingConfig.titles;
39148
39149             var ratingStates = angular.isDefined($attrs.ratingStates) ?
39150               $scope.$parent.$eval($attrs.ratingStates) :
39151               new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
39152             $scope.range = this.buildTemplateObjects(ratingStates);
39153           };
39154
39155           this.buildTemplateObjects = function(states) {
39156             for (var i = 0, n = states.length; i < n; i++) {
39157               states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
39158             }
39159             return states;
39160           };
39161
39162           this.getTitle = function(index) {
39163             if (index >= this.titles.length) {
39164               return index + 1;
39165             }
39166
39167             return this.titles[index];
39168           };
39169
39170           $scope.rate = function(value) {
39171             if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
39172               ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
39173               ngModelCtrl.$render();
39174             }
39175           };
39176
39177           $scope.enter = function(value) {
39178             if (!$scope.readonly) {
39179               $scope.value = value;
39180             }
39181             $scope.onHover({value: value});
39182           };
39183
39184           $scope.reset = function() {
39185             $scope.value = ngModelCtrl.$viewValue;
39186             $scope.onLeave();
39187           };
39188
39189           $scope.onKeydown = function(evt) {
39190             if (/(37|38|39|40)/.test(evt.which)) {
39191               evt.preventDefault();
39192               evt.stopPropagation();
39193               $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
39194             }
39195           };
39196
39197           this.render = function() {
39198             $scope.value = ngModelCtrl.$viewValue;
39199           };
39200         }])
39201
39202         .directive('uibRating', function() {
39203           return {
39204             require: ['uibRating', 'ngModel'],
39205             scope: {
39206               readonly: '=?',
39207               onHover: '&',
39208               onLeave: '&'
39209             },
39210             controller: 'UibRatingController',
39211             templateUrl: 'uib/template/rating/rating.html',
39212             replace: true,
39213             link: function(scope, element, attrs, ctrls) {
39214               var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39215               ratingCtrl.init(ngModelCtrl);
39216             }
39217           };
39218         });
39219
39220         angular.module('ui.bootstrap.tabs', [])
39221
39222         .controller('UibTabsetController', ['$scope', function ($scope) {
39223           var ctrl = this,
39224               tabs = ctrl.tabs = $scope.tabs = [];
39225
39226           ctrl.select = function(selectedTab) {
39227             angular.forEach(tabs, function(tab) {
39228               if (tab.active && tab !== selectedTab) {
39229                 tab.active = false;
39230                 tab.onDeselect();
39231                 selectedTab.selectCalled = false;
39232               }
39233             });
39234             selectedTab.active = true;
39235             // only call select if it has not already been called
39236             if (!selectedTab.selectCalled) {
39237               selectedTab.onSelect();
39238               selectedTab.selectCalled = true;
39239             }
39240           };
39241
39242           ctrl.addTab = function addTab(tab) {
39243             tabs.push(tab);
39244             // we can't run the select function on the first tab
39245             // since that would select it twice
39246             if (tabs.length === 1 && tab.active !== false) {
39247               tab.active = true;
39248             } else if (tab.active) {
39249               ctrl.select(tab);
39250             } else {
39251               tab.active = false;
39252             }
39253           };
39254
39255           ctrl.removeTab = function removeTab(tab) {
39256             var index = tabs.indexOf(tab);
39257             //Select a new tab if the tab to be removed is selected and not destroyed
39258             if (tab.active && tabs.length > 1 && !destroyed) {
39259               //If this is the last tab, select the previous tab. else, the next tab.
39260               var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
39261               ctrl.select(tabs[newActiveIndex]);
39262             }
39263             tabs.splice(index, 1);
39264           };
39265
39266           var destroyed;
39267           $scope.$on('$destroy', function() {
39268             destroyed = true;
39269           });
39270         }])
39271
39272         .directive('uibTabset', function() {
39273           return {
39274             transclude: true,
39275             replace: true,
39276             scope: {
39277               type: '@'
39278             },
39279             controller: 'UibTabsetController',
39280             templateUrl: 'uib/template/tabs/tabset.html',
39281             link: function(scope, element, attrs) {
39282               scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
39283               scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
39284             }
39285           };
39286         })
39287
39288         .directive('uibTab', ['$parse', function($parse) {
39289           return {
39290             require: '^uibTabset',
39291             replace: true,
39292             templateUrl: 'uib/template/tabs/tab.html',
39293             transclude: true,
39294             scope: {
39295               active: '=?',
39296               heading: '@',
39297               onSelect: '&select', //This callback is called in contentHeadingTransclude
39298                                   //once it inserts the tab's content into the dom
39299               onDeselect: '&deselect'
39300             },
39301             controller: function() {
39302               //Empty controller so other directives can require being 'under' a tab
39303             },
39304             controllerAs: 'tab',
39305             link: function(scope, elm, attrs, tabsetCtrl, transclude) {
39306               scope.$watch('active', function(active) {
39307                 if (active) {
39308                   tabsetCtrl.select(scope);
39309                 }
39310               });
39311
39312               scope.disabled = false;
39313               if (attrs.disable) {
39314                 scope.$parent.$watch($parse(attrs.disable), function(value) {
39315                   scope.disabled = !! value;
39316                 });
39317               }
39318
39319               scope.select = function() {
39320                 if (!scope.disabled) {
39321                   scope.active = true;
39322                 }
39323               };
39324
39325               tabsetCtrl.addTab(scope);
39326               scope.$on('$destroy', function() {
39327                 tabsetCtrl.removeTab(scope);
39328               });
39329
39330               //We need to transclude later, once the content container is ready.
39331               //when this link happens, we're inside a tab heading.
39332               scope.$transcludeFn = transclude;
39333             }
39334           };
39335         }])
39336
39337         .directive('uibTabHeadingTransclude', function() {
39338           return {
39339             restrict: 'A',
39340             require: '^uibTab',
39341             link: function(scope, elm) {
39342               scope.$watch('headingElement', function updateHeadingElement(heading) {
39343                 if (heading) {
39344                   elm.html('');
39345                   elm.append(heading);
39346                 }
39347               });
39348             }
39349           };
39350         })
39351
39352         .directive('uibTabContentTransclude', function() {
39353           return {
39354             restrict: 'A',
39355             require: '^uibTabset',
39356             link: function(scope, elm, attrs) {
39357               var tab = scope.$eval(attrs.uibTabContentTransclude);
39358
39359               //Now our tab is ready to be transcluded: both the tab heading area
39360               //and the tab content area are loaded.  Transclude 'em both.
39361               tab.$transcludeFn(tab.$parent, function(contents) {
39362                 angular.forEach(contents, function(node) {
39363                   if (isTabHeading(node)) {
39364                     //Let tabHeadingTransclude know.
39365                     tab.headingElement = node;
39366                   } else {
39367                     elm.append(node);
39368                   }
39369                 });
39370               });
39371             }
39372           };
39373
39374           function isTabHeading(node) {
39375             return node.tagName && (
39376               node.hasAttribute('uib-tab-heading') ||
39377               node.hasAttribute('data-uib-tab-heading') ||
39378               node.hasAttribute('x-uib-tab-heading') ||
39379               node.tagName.toLowerCase() === 'uib-tab-heading' ||
39380               node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
39381               node.tagName.toLowerCase() === 'x-uib-tab-heading'
39382             );
39383           }
39384         });
39385
39386         angular.module('ui.bootstrap.timepicker', [])
39387
39388         .constant('uibTimepickerConfig', {
39389           hourStep: 1,
39390           minuteStep: 1,
39391           secondStep: 1,
39392           showMeridian: true,
39393           showSeconds: false,
39394           meridians: null,
39395           readonlyInput: false,
39396           mousewheel: true,
39397           arrowkeys: true,
39398           showSpinners: true
39399         })
39400
39401         .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
39402           var selected = new Date(),
39403               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
39404               meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
39405
39406           $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
39407           $element.removeAttr('tabindex');
39408
39409           this.init = function(ngModelCtrl_, inputs) {
39410             ngModelCtrl = ngModelCtrl_;
39411             ngModelCtrl.$render = this.render;
39412
39413             ngModelCtrl.$formatters.unshift(function(modelValue) {
39414               return modelValue ? new Date(modelValue) : null;
39415             });
39416
39417             var hoursInputEl = inputs.eq(0),
39418                 minutesInputEl = inputs.eq(1),
39419                 secondsInputEl = inputs.eq(2);
39420
39421             var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
39422
39423             if (mousewheel) {
39424               this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39425             }
39426
39427             var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
39428             if (arrowkeys) {
39429               this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39430             }
39431
39432             $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
39433             this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39434           };
39435
39436           var hourStep = timepickerConfig.hourStep;
39437           if ($attrs.hourStep) {
39438             $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
39439               hourStep = parseInt(value, 10);
39440             });
39441           }
39442
39443           var minuteStep = timepickerConfig.minuteStep;
39444           if ($attrs.minuteStep) {
39445             $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
39446               minuteStep = parseInt(value, 10);
39447             });
39448           }
39449
39450           var min;
39451           $scope.$parent.$watch($parse($attrs.min), function(value) {
39452             var dt = new Date(value);
39453             min = isNaN(dt) ? undefined : dt;
39454           });
39455
39456           var max;
39457           $scope.$parent.$watch($parse($attrs.max), function(value) {
39458             var dt = new Date(value);
39459             max = isNaN(dt) ? undefined : dt;
39460           });
39461
39462           var disabled = false;
39463           if ($attrs.ngDisabled) {
39464             $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
39465               disabled = value;
39466             });
39467           }
39468
39469           $scope.noIncrementHours = function() {
39470             var incrementedSelected = addMinutes(selected, hourStep * 60);
39471             return disabled || incrementedSelected > max ||
39472               incrementedSelected < selected && incrementedSelected < min;
39473           };
39474
39475           $scope.noDecrementHours = function() {
39476             var decrementedSelected = addMinutes(selected, -hourStep * 60);
39477             return disabled || decrementedSelected < min ||
39478               decrementedSelected > selected && decrementedSelected > max;
39479           };
39480
39481           $scope.noIncrementMinutes = function() {
39482             var incrementedSelected = addMinutes(selected, minuteStep);
39483             return disabled || incrementedSelected > max ||
39484               incrementedSelected < selected && incrementedSelected < min;
39485           };
39486
39487           $scope.noDecrementMinutes = function() {
39488             var decrementedSelected = addMinutes(selected, -minuteStep);
39489             return disabled || decrementedSelected < min ||
39490               decrementedSelected > selected && decrementedSelected > max;
39491           };
39492
39493           $scope.noIncrementSeconds = function() {
39494             var incrementedSelected = addSeconds(selected, secondStep);
39495             return disabled || incrementedSelected > max ||
39496               incrementedSelected < selected && incrementedSelected < min;
39497           };
39498
39499           $scope.noDecrementSeconds = function() {
39500             var decrementedSelected = addSeconds(selected, -secondStep);
39501             return disabled || decrementedSelected < min ||
39502               decrementedSelected > selected && decrementedSelected > max;
39503           };
39504
39505           $scope.noToggleMeridian = function() {
39506             if (selected.getHours() < 12) {
39507               return disabled || addMinutes(selected, 12 * 60) > max;
39508             }
39509
39510             return disabled || addMinutes(selected, -12 * 60) < min;
39511           };
39512
39513           var secondStep = timepickerConfig.secondStep;
39514           if ($attrs.secondStep) {
39515             $scope.$parent.$watch($parse($attrs.secondStep), function(value) {
39516               secondStep = parseInt(value, 10);
39517             });
39518           }
39519
39520           $scope.showSeconds = timepickerConfig.showSeconds;
39521           if ($attrs.showSeconds) {
39522             $scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
39523               $scope.showSeconds = !!value;
39524             });
39525           }
39526
39527           // 12H / 24H mode
39528           $scope.showMeridian = timepickerConfig.showMeridian;
39529           if ($attrs.showMeridian) {
39530             $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
39531               $scope.showMeridian = !!value;
39532
39533               if (ngModelCtrl.$error.time) {
39534                 // Evaluate from template
39535                 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
39536                 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39537                   selected.setHours(hours);
39538                   refresh();
39539                 }
39540               } else {
39541                 updateTemplate();
39542               }
39543             });
39544           }
39545
39546           // Get $scope.hours in 24H mode if valid
39547           function getHoursFromTemplate() {
39548             var hours = parseInt($scope.hours, 10);
39549             var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
39550               hours >= 0 && hours < 24;
39551             if (!valid) {
39552               return undefined;
39553             }
39554
39555             if ($scope.showMeridian) {
39556               if (hours === 12) {
39557                 hours = 0;
39558               }
39559               if ($scope.meridian === meridians[1]) {
39560                 hours = hours + 12;
39561               }
39562             }
39563             return hours;
39564           }
39565
39566           function getMinutesFromTemplate() {
39567             var minutes = parseInt($scope.minutes, 10);
39568             return minutes >= 0 && minutes < 60 ? minutes : undefined;
39569           }
39570
39571           function getSecondsFromTemplate() {
39572             var seconds = parseInt($scope.seconds, 10);
39573             return seconds >= 0 && seconds < 60 ? seconds : undefined;
39574           }
39575
39576           function pad(value) {
39577             if (value === null) {
39578               return '';
39579             }
39580
39581             return angular.isDefined(value) && value.toString().length < 2 ?
39582               '0' + value : value.toString();
39583           }
39584
39585           // Respond on mousewheel spin
39586           this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39587             var isScrollingUp = function(e) {
39588               if (e.originalEvent) {
39589                 e = e.originalEvent;
39590               }
39591               //pick correct delta variable depending on event
39592               var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
39593               return e.detail || delta > 0;
39594             };
39595
39596             hoursInputEl.bind('mousewheel wheel', function(e) {
39597               if (!disabled) {
39598                 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
39599               }
39600               e.preventDefault();
39601             });
39602
39603             minutesInputEl.bind('mousewheel wheel', function(e) {
39604               if (!disabled) {
39605                 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
39606               }
39607               e.preventDefault();
39608             });
39609
39610              secondsInputEl.bind('mousewheel wheel', function(e) {
39611               if (!disabled) {
39612                 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
39613               }
39614               e.preventDefault();
39615             });
39616           };
39617
39618           // Respond on up/down arrowkeys
39619           this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39620             hoursInputEl.bind('keydown', function(e) {
39621               if (!disabled) {
39622                 if (e.which === 38) { // up
39623                   e.preventDefault();
39624                   $scope.incrementHours();
39625                   $scope.$apply();
39626                 } else if (e.which === 40) { // down
39627                   e.preventDefault();
39628                   $scope.decrementHours();
39629                   $scope.$apply();
39630                 }
39631               }
39632             });
39633
39634             minutesInputEl.bind('keydown', function(e) {
39635               if (!disabled) {
39636                 if (e.which === 38) { // up
39637                   e.preventDefault();
39638                   $scope.incrementMinutes();
39639                   $scope.$apply();
39640                 } else if (e.which === 40) { // down
39641                   e.preventDefault();
39642                   $scope.decrementMinutes();
39643                   $scope.$apply();
39644                 }
39645               }
39646             });
39647
39648             secondsInputEl.bind('keydown', function(e) {
39649               if (!disabled) {
39650                 if (e.which === 38) { // up
39651                   e.preventDefault();
39652                   $scope.incrementSeconds();
39653                   $scope.$apply();
39654                 } else if (e.which === 40) { // down
39655                   e.preventDefault();
39656                   $scope.decrementSeconds();
39657                   $scope.$apply();
39658                 }
39659               }
39660             });
39661           };
39662
39663           this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39664             if ($scope.readonlyInput) {
39665               $scope.updateHours = angular.noop;
39666               $scope.updateMinutes = angular.noop;
39667               $scope.updateSeconds = angular.noop;
39668               return;
39669             }
39670
39671             var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
39672               ngModelCtrl.$setViewValue(null);
39673               ngModelCtrl.$setValidity('time', false);
39674               if (angular.isDefined(invalidHours)) {
39675                 $scope.invalidHours = invalidHours;
39676               }
39677
39678               if (angular.isDefined(invalidMinutes)) {
39679                 $scope.invalidMinutes = invalidMinutes;
39680               }
39681
39682               if (angular.isDefined(invalidSeconds)) {
39683                 $scope.invalidSeconds = invalidSeconds;
39684               }
39685             };
39686
39687             $scope.updateHours = function() {
39688               var hours = getHoursFromTemplate(),
39689                 minutes = getMinutesFromTemplate();
39690
39691               ngModelCtrl.$setDirty();
39692
39693               if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39694                 selected.setHours(hours);
39695                 selected.setMinutes(minutes);
39696                 if (selected < min || selected > max) {
39697                   invalidate(true);
39698                 } else {
39699                   refresh('h');
39700                 }
39701               } else {
39702                 invalidate(true);
39703               }
39704             };
39705
39706             hoursInputEl.bind('blur', function(e) {
39707               ngModelCtrl.$setTouched();
39708               if ($scope.hours === null || $scope.hours === '') {
39709                 invalidate(true);
39710               } else if (!$scope.invalidHours && $scope.hours < 10) {
39711                 $scope.$apply(function() {
39712                   $scope.hours = pad($scope.hours);
39713                 });
39714               }
39715             });
39716
39717             $scope.updateMinutes = function() {
39718               var minutes = getMinutesFromTemplate(),
39719                 hours = getHoursFromTemplate();
39720
39721               ngModelCtrl.$setDirty();
39722
39723               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39724                 selected.setHours(hours);
39725                 selected.setMinutes(minutes);
39726                 if (selected < min || selected > max) {
39727                   invalidate(undefined, true);
39728                 } else {
39729                   refresh('m');
39730                 }
39731               } else {
39732                 invalidate(undefined, true);
39733               }
39734             };
39735
39736             minutesInputEl.bind('blur', function(e) {
39737               ngModelCtrl.$setTouched();
39738               if ($scope.minutes === null) {
39739                 invalidate(undefined, true);
39740               } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
39741                 $scope.$apply(function() {
39742                   $scope.minutes = pad($scope.minutes);
39743                 });
39744               }
39745             });
39746
39747             $scope.updateSeconds = function() {
39748               var seconds = getSecondsFromTemplate();
39749
39750               ngModelCtrl.$setDirty();
39751
39752               if (angular.isDefined(seconds)) {
39753                 selected.setSeconds(seconds);
39754                 refresh('s');
39755               } else {
39756                 invalidate(undefined, undefined, true);
39757               }
39758             };
39759
39760             secondsInputEl.bind('blur', function(e) {
39761               if (!$scope.invalidSeconds && $scope.seconds < 10) {
39762                 $scope.$apply( function() {
39763                   $scope.seconds = pad($scope.seconds);
39764                 });
39765               }
39766             });
39767
39768           };
39769
39770           this.render = function() {
39771             var date = ngModelCtrl.$viewValue;
39772
39773             if (isNaN(date)) {
39774               ngModelCtrl.$setValidity('time', false);
39775               $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.');
39776             } else {
39777               if (date) {
39778                 selected = date;
39779               }
39780
39781               if (selected < min || selected > max) {
39782                 ngModelCtrl.$setValidity('time', false);
39783                 $scope.invalidHours = true;
39784                 $scope.invalidMinutes = true;
39785               } else {
39786                 makeValid();
39787               }
39788               updateTemplate();
39789             }
39790           };
39791
39792           // Call internally when we know that model is valid.
39793           function refresh(keyboardChange) {
39794             makeValid();
39795             ngModelCtrl.$setViewValue(new Date(selected));
39796             updateTemplate(keyboardChange);
39797           }
39798
39799           function makeValid() {
39800             ngModelCtrl.$setValidity('time', true);
39801             $scope.invalidHours = false;
39802             $scope.invalidMinutes = false;
39803             $scope.invalidSeconds = false;
39804           }
39805
39806           function updateTemplate(keyboardChange) {
39807             if (!ngModelCtrl.$modelValue) {
39808               $scope.hours = null;
39809               $scope.minutes = null;
39810               $scope.seconds = null;
39811               $scope.meridian = meridians[0];
39812             } else {
39813               var hours = selected.getHours(),
39814                 minutes = selected.getMinutes(),
39815                 seconds = selected.getSeconds();
39816
39817               if ($scope.showMeridian) {
39818                 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
39819               }
39820
39821               $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
39822               if (keyboardChange !== 'm') {
39823                 $scope.minutes = pad(minutes);
39824               }
39825               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39826
39827               if (keyboardChange !== 's') {
39828                 $scope.seconds = pad(seconds);
39829               }
39830               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39831             }
39832           }
39833
39834           function addSecondsToSelected(seconds) {
39835             selected = addSeconds(selected, seconds);
39836             refresh();
39837           }
39838
39839           function addMinutes(selected, minutes) {
39840             return addSeconds(selected, minutes*60);
39841           }
39842
39843           function addSeconds(date, seconds) {
39844             var dt = new Date(date.getTime() + seconds * 1000);
39845             var newDate = new Date(date);
39846             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
39847             return newDate;
39848           }
39849
39850           $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
39851             $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
39852
39853           $scope.incrementHours = function() {
39854             if (!$scope.noIncrementHours()) {
39855               addSecondsToSelected(hourStep * 60 * 60);
39856             }
39857           };
39858
39859           $scope.decrementHours = function() {
39860             if (!$scope.noDecrementHours()) {
39861               addSecondsToSelected(-hourStep * 60 * 60);
39862             }
39863           };
39864
39865           $scope.incrementMinutes = function() {
39866             if (!$scope.noIncrementMinutes()) {
39867               addSecondsToSelected(minuteStep * 60);
39868             }
39869           };
39870
39871           $scope.decrementMinutes = function() {
39872             if (!$scope.noDecrementMinutes()) {
39873               addSecondsToSelected(-minuteStep * 60);
39874             }
39875           };
39876
39877           $scope.incrementSeconds = function() {
39878             if (!$scope.noIncrementSeconds()) {
39879               addSecondsToSelected(secondStep);
39880             }
39881           };
39882
39883           $scope.decrementSeconds = function() {
39884             if (!$scope.noDecrementSeconds()) {
39885               addSecondsToSelected(-secondStep);
39886             }
39887           };
39888
39889           $scope.toggleMeridian = function() {
39890             var minutes = getMinutesFromTemplate(),
39891                 hours = getHoursFromTemplate();
39892
39893             if (!$scope.noToggleMeridian()) {
39894               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39895                 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
39896               } else {
39897                 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
39898               }
39899             }
39900           };
39901
39902           $scope.blur = function() {
39903             ngModelCtrl.$setTouched();
39904           };
39905         }])
39906
39907         .directive('uibTimepicker', function() {
39908           return {
39909             require: ['uibTimepicker', '?^ngModel'],
39910             controller: 'UibTimepickerController',
39911             controllerAs: 'timepicker',
39912             replace: true,
39913             scope: {},
39914             templateUrl: function(element, attrs) {
39915               return attrs.templateUrl || 'uib/template/timepicker/timepicker.html';
39916             },
39917             link: function(scope, element, attrs, ctrls) {
39918               var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39919
39920               if (ngModelCtrl) {
39921                 timepickerCtrl.init(ngModelCtrl, element.find('input'));
39922               }
39923             }
39924           };
39925         });
39926
39927         angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
39928
39929         /**
39930          * A helper service that can parse typeahead's syntax (string provided by users)
39931          * Extracted to a separate service for ease of unit testing
39932          */
39933           .factory('uibTypeaheadParser', ['$parse', function($parse) {
39934             //                      00000111000000000000022200000000000000003333333333333330000000000044000
39935             var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
39936             return {
39937               parse: function(input) {
39938                 var match = input.match(TYPEAHEAD_REGEXP);
39939                 if (!match) {
39940                   throw new Error(
39941                     'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
39942                       ' but got "' + input + '".');
39943                 }
39944
39945                 return {
39946                   itemName: match[3],
39947                   source: $parse(match[4]),
39948                   viewMapper: $parse(match[2] || match[1]),
39949                   modelMapper: $parse(match[1])
39950                 };
39951               }
39952             };
39953           }])
39954
39955           .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
39956             function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
39957             var HOT_KEYS = [9, 13, 27, 38, 40];
39958             var eventDebounceTime = 200;
39959             var modelCtrl, ngModelOptions;
39960             //SUPPORTED ATTRIBUTES (OPTIONS)
39961
39962             //minimal no of characters that needs to be entered before typeahead kicks-in
39963             var minLength = originalScope.$eval(attrs.typeaheadMinLength);
39964             if (!minLength && minLength !== 0) {
39965               minLength = 1;
39966             }
39967
39968             //minimal wait time after last character typed before typeahead kicks-in
39969             var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
39970
39971             //should it restrict model values to the ones selected from the popup only?
39972             var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
39973             originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
39974               isEditable = newVal !== false;
39975             });
39976
39977             //binding to a variable that indicates if matches are being retrieved asynchronously
39978             var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
39979
39980             //a callback executed when a match is selected
39981             var onSelectCallback = $parse(attrs.typeaheadOnSelect);
39982
39983             //should it select highlighted popup value when losing focus?
39984             var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
39985
39986             //binding to a variable that indicates if there were no results after the query is completed
39987             var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
39988
39989             var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
39990
39991             var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
39992
39993             var appendTo = attrs.typeaheadAppendTo ?
39994               originalScope.$eval(attrs.typeaheadAppendTo) : null;
39995
39996             var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
39997
39998             //If input matches an item of the list exactly, select it automatically
39999             var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
40000
40001             //binding to a variable that indicates if dropdown is open
40002             var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
40003
40004             var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
40005
40006             //INTERNAL VARIABLES
40007
40008             //model setter executed upon match selection
40009             var parsedModel = $parse(attrs.ngModel);
40010             var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
40011             var $setModelValue = function(scope, newValue) {
40012               if (angular.isFunction(parsedModel(originalScope)) &&
40013                 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
40014                 return invokeModelSetter(scope, {$$$p: newValue});
40015               }
40016
40017               return parsedModel.assign(scope, newValue);
40018             };
40019
40020             //expressions used by typeahead
40021             var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
40022
40023             var hasFocus;
40024
40025             //Used to avoid bug in iOS webview where iOS keyboard does not fire
40026             //mousedown & mouseup events
40027             //Issue #3699
40028             var selected;
40029
40030             //create a child scope for the typeahead directive so we are not polluting original scope
40031             //with typeahead-specific data (matches, query etc.)
40032             var scope = originalScope.$new();
40033             var offDestroy = originalScope.$on('$destroy', function() {
40034               scope.$destroy();
40035             });
40036             scope.$on('$destroy', offDestroy);
40037
40038             // WAI-ARIA
40039             var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
40040             element.attr({
40041               'aria-autocomplete': 'list',
40042               'aria-expanded': false,
40043               'aria-owns': popupId
40044             });
40045
40046             var inputsContainer, hintInputElem;
40047             //add read-only input to show hint
40048             if (showHint) {
40049               inputsContainer = angular.element('<div></div>');
40050               inputsContainer.css('position', 'relative');
40051               element.after(inputsContainer);
40052               hintInputElem = element.clone();
40053               hintInputElem.attr('placeholder', '');
40054               hintInputElem.val('');
40055               hintInputElem.css({
40056                 'position': 'absolute',
40057                 'top': '0px',
40058                 'left': '0px',
40059                 'border-color': 'transparent',
40060                 'box-shadow': 'none',
40061                 'opacity': 1,
40062                 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
40063                 'color': '#999'
40064               });
40065               element.css({
40066                 'position': 'relative',
40067                 'vertical-align': 'top',
40068                 'background-color': 'transparent'
40069               });
40070               inputsContainer.append(hintInputElem);
40071               hintInputElem.after(element);
40072             }
40073
40074             //pop-up element used to display matches
40075             var popUpEl = angular.element('<div uib-typeahead-popup></div>');
40076             popUpEl.attr({
40077               id: popupId,
40078               matches: 'matches',
40079               active: 'activeIdx',
40080               select: 'select(activeIdx, evt)',
40081               'move-in-progress': 'moveInProgress',
40082               query: 'query',
40083               position: 'position',
40084               'assign-is-open': 'assignIsOpen(isOpen)',
40085               debounce: 'debounceUpdate'
40086             });
40087             //custom item template
40088             if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
40089               popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
40090             }
40091
40092             if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
40093               popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
40094             }
40095
40096             var resetHint = function() {
40097               if (showHint) {
40098                 hintInputElem.val('');
40099               }
40100             };
40101
40102             var resetMatches = function() {
40103               scope.matches = [];
40104               scope.activeIdx = -1;
40105               element.attr('aria-expanded', false);
40106               resetHint();
40107             };
40108
40109             var getMatchId = function(index) {
40110               return popupId + '-option-' + index;
40111             };
40112
40113             // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
40114             // This attribute is added or removed automatically when the `activeIdx` changes.
40115             scope.$watch('activeIdx', function(index) {
40116               if (index < 0) {
40117                 element.removeAttr('aria-activedescendant');
40118               } else {
40119                 element.attr('aria-activedescendant', getMatchId(index));
40120               }
40121             });
40122
40123             var inputIsExactMatch = function(inputValue, index) {
40124               if (scope.matches.length > index && inputValue) {
40125                 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
40126               }
40127
40128               return false;
40129             };
40130
40131             var getMatchesAsync = function(inputValue, evt) {
40132               var locals = {$viewValue: inputValue};
40133               isLoadingSetter(originalScope, true);
40134               isNoResultsSetter(originalScope, false);
40135               $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
40136                 //it might happen that several async queries were in progress if a user were typing fast
40137                 //but we are interested only in responses that correspond to the current view value
40138                 var onCurrentRequest = inputValue === modelCtrl.$viewValue;
40139                 if (onCurrentRequest && hasFocus) {
40140                   if (matches && matches.length > 0) {
40141                     scope.activeIdx = focusFirst ? 0 : -1;
40142                     isNoResultsSetter(originalScope, false);
40143                     scope.matches.length = 0;
40144
40145                     //transform labels
40146                     for (var i = 0; i < matches.length; i++) {
40147                       locals[parserResult.itemName] = matches[i];
40148                       scope.matches.push({
40149                         id: getMatchId(i),
40150                         label: parserResult.viewMapper(scope, locals),
40151                         model: matches[i]
40152                       });
40153                     }
40154
40155                     scope.query = inputValue;
40156                     //position pop-up with matches - we need to re-calculate its position each time we are opening a window
40157                     //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
40158                     //due to other elements being rendered
40159                     recalculatePosition();
40160
40161                     element.attr('aria-expanded', true);
40162
40163                     //Select the single remaining option if user input matches
40164                     if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
40165                       if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40166                         $$debounce(function() {
40167                           scope.select(0, evt);
40168                         }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40169                       } else {
40170                         scope.select(0, evt);
40171                       }
40172                     }
40173
40174                     if (showHint) {
40175                       var firstLabel = scope.matches[0].label;
40176                       if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
40177                         hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
40178                       }
40179                       else {
40180                         hintInputElem.val('');
40181                       }
40182                     }
40183                   } else {
40184                     resetMatches();
40185                     isNoResultsSetter(originalScope, true);
40186                   }
40187                 }
40188                 if (onCurrentRequest) {
40189                   isLoadingSetter(originalScope, false);
40190                 }
40191               }, function() {
40192                 resetMatches();
40193                 isLoadingSetter(originalScope, false);
40194                 isNoResultsSetter(originalScope, true);
40195               });
40196             };
40197
40198             // bind events only if appendToBody params exist - performance feature
40199             if (appendToBody) {
40200               angular.element($window).on('resize', fireRecalculating);
40201               $document.find('body').on('scroll', fireRecalculating);
40202             }
40203
40204             // Declare the debounced function outside recalculating for
40205             // proper debouncing
40206             var debouncedRecalculate = $$debounce(function() {
40207               // if popup is visible
40208               if (scope.matches.length) {
40209                 recalculatePosition();
40210               }
40211
40212               scope.moveInProgress = false;
40213             }, eventDebounceTime);
40214
40215             // Default progress type
40216             scope.moveInProgress = false;
40217
40218             function fireRecalculating() {
40219               if (!scope.moveInProgress) {
40220                 scope.moveInProgress = true;
40221                 scope.$digest();
40222               }
40223
40224               debouncedRecalculate();
40225             }
40226
40227             // recalculate actual position and set new values to scope
40228             // after digest loop is popup in right position
40229             function recalculatePosition() {
40230               scope.position = appendToBody ? $position.offset(element) : $position.position(element);
40231               scope.position.top += element.prop('offsetHeight');
40232             }
40233
40234             //we need to propagate user's query so we can higlight matches
40235             scope.query = undefined;
40236
40237             //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
40238             var timeoutPromise;
40239
40240             var scheduleSearchWithTimeout = function(inputValue) {
40241               timeoutPromise = $timeout(function() {
40242                 getMatchesAsync(inputValue);
40243               }, waitTime);
40244             };
40245
40246             var cancelPreviousTimeout = function() {
40247               if (timeoutPromise) {
40248                 $timeout.cancel(timeoutPromise);
40249               }
40250             };
40251
40252             resetMatches();
40253
40254             scope.assignIsOpen = function (isOpen) {
40255               isOpenSetter(originalScope, isOpen);
40256             };
40257
40258             scope.select = function(activeIdx, evt) {
40259               //called from within the $digest() cycle
40260               var locals = {};
40261               var model, item;
40262
40263               selected = true;
40264               locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
40265               model = parserResult.modelMapper(originalScope, locals);
40266               $setModelValue(originalScope, model);
40267               modelCtrl.$setValidity('editable', true);
40268               modelCtrl.$setValidity('parse', true);
40269
40270               onSelectCallback(originalScope, {
40271                 $item: item,
40272                 $model: model,
40273                 $label: parserResult.viewMapper(originalScope, locals),
40274                 $event: evt
40275               });
40276
40277               resetMatches();
40278
40279               //return focus to the input element if a match was selected via a mouse click event
40280               // use timeout to avoid $rootScope:inprog error
40281               if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
40282                 $timeout(function() { element[0].focus(); }, 0, false);
40283               }
40284             };
40285
40286             //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
40287             element.on('keydown', function(evt) {
40288               //typeahead is open and an "interesting" key was pressed
40289               if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
40290                 return;
40291               }
40292
40293               // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
40294               if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
40295                 resetMatches();
40296                 scope.$digest();
40297                 return;
40298               }
40299
40300               evt.preventDefault();
40301
40302               switch (evt.which) {
40303                 case 9:
40304                 case 13:
40305                   scope.$apply(function () {
40306                     if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40307                       $$debounce(function() {
40308                         scope.select(scope.activeIdx, evt);
40309                       }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40310                     } else {
40311                       scope.select(scope.activeIdx, evt);
40312                     }
40313                   });
40314                   break;
40315                 case 27:
40316                   evt.stopPropagation();
40317
40318                   resetMatches();
40319                   scope.$digest();
40320                   break;
40321                 case 38:
40322                   scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
40323                   scope.$digest();
40324                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40325                   break;
40326                 case 40:
40327                   scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
40328                   scope.$digest();
40329                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40330                   break;
40331               }
40332             });
40333
40334             element.bind('focus', function (evt) {
40335               hasFocus = true;
40336               if (minLength === 0 && !modelCtrl.$viewValue) {
40337                 $timeout(function() {
40338                   getMatchesAsync(modelCtrl.$viewValue, evt);
40339                 }, 0);
40340               }
40341             });
40342
40343             element.bind('blur', function(evt) {
40344               if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
40345                 selected = true;
40346                 scope.$apply(function() {
40347                   if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
40348                     $$debounce(function() {
40349                       scope.select(scope.activeIdx, evt);
40350                     }, scope.debounceUpdate.blur);
40351                   } else {
40352                     scope.select(scope.activeIdx, evt);
40353                   }
40354                 });
40355               }
40356               if (!isEditable && modelCtrl.$error.editable) {
40357                 modelCtrl.$viewValue = '';
40358                 element.val('');
40359               }
40360               hasFocus = false;
40361               selected = false;
40362             });
40363
40364             // Keep reference to click handler to unbind it.
40365             var dismissClickHandler = function(evt) {
40366               // Issue #3973
40367               // Firefox treats right click as a click on document
40368               if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
40369                 resetMatches();
40370                 if (!$rootScope.$$phase) {
40371                   scope.$digest();
40372                 }
40373               }
40374             };
40375
40376             $document.on('click', dismissClickHandler);
40377
40378             originalScope.$on('$destroy', function() {
40379               $document.off('click', dismissClickHandler);
40380               if (appendToBody || appendTo) {
40381                 $popup.remove();
40382               }
40383
40384               if (appendToBody) {
40385                 angular.element($window).off('resize', fireRecalculating);
40386                 $document.find('body').off('scroll', fireRecalculating);
40387               }
40388               // Prevent jQuery cache memory leak
40389               popUpEl.remove();
40390
40391               if (showHint) {
40392                   inputsContainer.remove();
40393               }
40394             });
40395
40396             var $popup = $compile(popUpEl)(scope);
40397
40398             if (appendToBody) {
40399               $document.find('body').append($popup);
40400             } else if (appendTo) {
40401               angular.element(appendTo).eq(0).append($popup);
40402             } else {
40403               element.after($popup);
40404             }
40405
40406             this.init = function(_modelCtrl, _ngModelOptions) {
40407               modelCtrl = _modelCtrl;
40408               ngModelOptions = _ngModelOptions;
40409
40410               scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
40411
40412               //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
40413               //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
40414               modelCtrl.$parsers.unshift(function(inputValue) {
40415                 hasFocus = true;
40416
40417                 if (minLength === 0 || inputValue && inputValue.length >= minLength) {
40418                   if (waitTime > 0) {
40419                     cancelPreviousTimeout();
40420                     scheduleSearchWithTimeout(inputValue);
40421                   } else {
40422                     getMatchesAsync(inputValue);
40423                   }
40424                 } else {
40425                   isLoadingSetter(originalScope, false);
40426                   cancelPreviousTimeout();
40427                   resetMatches();
40428                 }
40429
40430                 if (isEditable) {
40431                   return inputValue;
40432                 }
40433
40434                 if (!inputValue) {
40435                   // Reset in case user had typed something previously.
40436                   modelCtrl.$setValidity('editable', true);
40437                   return null;
40438                 }
40439
40440                 modelCtrl.$setValidity('editable', false);
40441                 return undefined;
40442               });
40443
40444               modelCtrl.$formatters.push(function(modelValue) {
40445                 var candidateViewValue, emptyViewValue;
40446                 var locals = {};
40447
40448                 // The validity may be set to false via $parsers (see above) if
40449                 // the model is restricted to selected values. If the model
40450                 // is set manually it is considered to be valid.
40451                 if (!isEditable) {
40452                   modelCtrl.$setValidity('editable', true);
40453                 }
40454
40455                 if (inputFormatter) {
40456                   locals.$model = modelValue;
40457                   return inputFormatter(originalScope, locals);
40458                 }
40459
40460                 //it might happen that we don't have enough info to properly render input value
40461                 //we need to check for this situation and simply return model value if we can't apply custom formatting
40462                 locals[parserResult.itemName] = modelValue;
40463                 candidateViewValue = parserResult.viewMapper(originalScope, locals);
40464                 locals[parserResult.itemName] = undefined;
40465                 emptyViewValue = parserResult.viewMapper(originalScope, locals);
40466
40467                 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
40468               });
40469             };
40470           }])
40471
40472           .directive('uibTypeahead', function() {
40473             return {
40474               controller: 'UibTypeaheadController',
40475               require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
40476               link: function(originalScope, element, attrs, ctrls) {
40477                 ctrls[2].init(ctrls[0], ctrls[1]);
40478               }
40479             };
40480           })
40481
40482           .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
40483             return {
40484               scope: {
40485                 matches: '=',
40486                 query: '=',
40487                 active: '=',
40488                 position: '&',
40489                 moveInProgress: '=',
40490                 select: '&',
40491                 assignIsOpen: '&',
40492                 debounce: '&'
40493               },
40494               replace: true,
40495               templateUrl: function(element, attrs) {
40496                 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
40497               },
40498               link: function(scope, element, attrs) {
40499                 scope.templateUrl = attrs.templateUrl;
40500
40501                 scope.isOpen = function() {
40502                   var isDropdownOpen = scope.matches.length > 0;
40503                   scope.assignIsOpen({ isOpen: isDropdownOpen });
40504                   return isDropdownOpen;
40505                 };
40506
40507                 scope.isActive = function(matchIdx) {
40508                   return scope.active === matchIdx;
40509                 };
40510
40511                 scope.selectActive = function(matchIdx) {
40512                   scope.active = matchIdx;
40513                 };
40514
40515                 scope.selectMatch = function(activeIdx, evt) {
40516                   var debounce = scope.debounce();
40517                   if (angular.isNumber(debounce) || angular.isObject(debounce)) {
40518                     $$debounce(function() {
40519                       scope.select({activeIdx: activeIdx, evt: evt});
40520                     }, angular.isNumber(debounce) ? debounce : debounce['default']);
40521                   } else {
40522                     scope.select({activeIdx: activeIdx, evt: evt});
40523                   }
40524                 };
40525               }
40526             };
40527           }])
40528
40529           .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
40530             return {
40531               scope: {
40532                 index: '=',
40533                 match: '=',
40534                 query: '='
40535               },
40536               link: function(scope, element, attrs) {
40537                 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
40538                 $templateRequest(tplUrl).then(function(tplContent) {
40539                   var tplEl = angular.element(tplContent.trim());
40540                   element.replaceWith(tplEl);
40541                   $compile(tplEl)(scope);
40542                 });
40543               }
40544             };
40545           }])
40546
40547           .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
40548             var isSanitizePresent;
40549             isSanitizePresent = $injector.has('$sanitize');
40550
40551             function escapeRegexp(queryToEscape) {
40552               // Regex: capture the whole query string and replace it with the string that will be used to match
40553               // the results, for example if the capture is "a" the result will be \a
40554               return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
40555             }
40556
40557             function containsHtml(matchItem) {
40558               return /<.*>/g.test(matchItem);
40559             }
40560
40561             return function(matchItem, query) {
40562               if (!isSanitizePresent && containsHtml(matchItem)) {
40563                 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
40564               }
40565               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
40566               if (!isSanitizePresent) {
40567                 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
40568               }
40569               return matchItem;
40570             };
40571           }]);
40572
40573         angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
40574           $templateCache.put("uib/template/accordion/accordion-group.html",
40575             "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
40576             "  <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
40577             "    <h4 class=\"panel-title\">\n" +
40578             "      <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" +
40579             "    </h4>\n" +
40580             "  </div>\n" +
40581             "  <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
40582             "     <div class=\"panel-body\" ng-transclude></div>\n" +
40583             "  </div>\n" +
40584             "</div>\n" +
40585             "");
40586         }]);
40587
40588         angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
40589           $templateCache.put("uib/template/accordion/accordion.html",
40590             "<div class=\"panel-group\" ng-transclude></div>");
40591         }]);
40592
40593         angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
40594           $templateCache.put("uib/template/alert/alert.html",
40595             "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
40596             "    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
40597             "        <span aria-hidden=\"true\">&times;</span>\n" +
40598             "        <span class=\"sr-only\">Close</span>\n" +
40599             "    </button>\n" +
40600             "    <div ng-transclude></div>\n" +
40601             "</div>\n" +
40602             "");
40603         }]);
40604
40605         angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
40606           $templateCache.put("uib/template/carousel/carousel.html",
40607             "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
40608             "  <div class=\"carousel-inner\" ng-transclude></div>\n" +
40609             "  <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" +
40610             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
40611             "    <span class=\"sr-only\">previous</span>\n" +
40612             "  </a>\n" +
40613             "  <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" +
40614             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
40615             "    <span class=\"sr-only\">next</span>\n" +
40616             "  </a>\n" +
40617             "  <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
40618             "    <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
40619             "      <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
40620             "    </li>\n" +
40621             "  </ol>\n" +
40622             "</div>");
40623         }]);
40624
40625         angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
40626           $templateCache.put("uib/template/carousel/slide.html",
40627             "<div ng-class=\"{\n" +
40628             "    'active': active\n" +
40629             "  }\" class=\"item text-center\" ng-transclude></div>\n" +
40630             "");
40631         }]);
40632
40633         angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
40634           $templateCache.put("uib/template/datepicker/datepicker.html",
40635             "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
40636             "  <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
40637             "  <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
40638             "  <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
40639             "</div>");
40640         }]);
40641
40642         angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
40643           $templateCache.put("uib/template/datepicker/day.html",
40644             "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40645             "  <thead>\n" +
40646             "    <tr>\n" +
40647             "      <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" +
40648             "      <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" +
40649             "      <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" +
40650             "    </tr>\n" +
40651             "    <tr>\n" +
40652             "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
40653             "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
40654             "    </tr>\n" +
40655             "  </thead>\n" +
40656             "  <tbody>\n" +
40657             "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
40658             "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
40659             "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
40660             "        id=\"{{::dt.uid}}\"\n" +
40661             "        ng-class=\"::dt.customClass\">\n" +
40662             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" +
40663             "          uib-is-class=\"\n" +
40664             "            'btn-info' for selectedDt,\n" +
40665             "            'active' for activeDt\n" +
40666             "            on dt\"\n" +
40667             "          ng-click=\"select(dt.date)\"\n" +
40668             "          ng-disabled=\"::dt.disabled\"\n" +
40669             "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40670             "      </td>\n" +
40671             "    </tr>\n" +
40672             "  </tbody>\n" +
40673             "</table>\n" +
40674             "");
40675         }]);
40676
40677         angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
40678           $templateCache.put("uib/template/datepicker/month.html",
40679             "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40680             "  <thead>\n" +
40681             "    <tr>\n" +
40682             "      <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" +
40683             "      <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" +
40684             "      <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" +
40685             "    </tr>\n" +
40686             "  </thead>\n" +
40687             "  <tbody>\n" +
40688             "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
40689             "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
40690             "        id=\"{{::dt.uid}}\"\n" +
40691             "        ng-class=\"::dt.customClass\">\n" +
40692             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40693             "          uib-is-class=\"\n" +
40694             "            'btn-info' for selectedDt,\n" +
40695             "            'active' for activeDt\n" +
40696             "            on dt\"\n" +
40697             "          ng-click=\"select(dt.date)\"\n" +
40698             "          ng-disabled=\"::dt.disabled\"\n" +
40699             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40700             "      </td>\n" +
40701             "    </tr>\n" +
40702             "  </tbody>\n" +
40703             "</table>\n" +
40704             "");
40705         }]);
40706
40707         angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
40708           $templateCache.put("uib/template/datepicker/popup.html",
40709             "<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" +
40710             "   <li ng-transclude></li>\n" +
40711             "   <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" +
40712             "           <span class=\"btn-group pull-left\">\n" +
40713             "                   <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
40714             "                   <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
40715             "           </span>\n" +
40716             "           <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
40717             "   </li>\n" +
40718             "</ul>\n" +
40719             "");
40720         }]);
40721
40722         angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
40723           $templateCache.put("uib/template/datepicker/year.html",
40724             "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40725             "  <thead>\n" +
40726             "    <tr>\n" +
40727             "      <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" +
40728             "      <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" +
40729             "      <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" +
40730             "    </tr>\n" +
40731             "  </thead>\n" +
40732             "  <tbody>\n" +
40733             "    <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
40734             "      <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
40735             "        id=\"{{::dt.uid}}\"\n" +
40736             "        ng-class=\"::dt.customClass\">\n" +
40737             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40738             "          uib-is-class=\"\n" +
40739             "            'btn-info' for selectedDt,\n" +
40740             "            'active' for activeDt\n" +
40741             "            on dt\"\n" +
40742             "          ng-click=\"select(dt.date)\"\n" +
40743             "          ng-disabled=\"::dt.disabled\"\n" +
40744             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40745             "      </td>\n" +
40746             "    </tr>\n" +
40747             "  </tbody>\n" +
40748             "</table>\n" +
40749             "");
40750         }]);
40751
40752         angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
40753           $templateCache.put("uib/template/modal/backdrop.html",
40754             "<div class=\"modal-backdrop\"\n" +
40755             "     uib-modal-animation-class=\"fade\"\n" +
40756             "     modal-in-class=\"in\"\n" +
40757             "     ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
40758             "></div>\n" +
40759             "");
40760         }]);
40761
40762         angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
40763           $templateCache.put("uib/template/modal/window.html",
40764             "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
40765             "    uib-modal-animation-class=\"fade\"\n" +
40766             "    modal-in-class=\"in\"\n" +
40767             "    ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
40768             "    <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
40769             "</div>\n" +
40770             "");
40771         }]);
40772
40773         angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
40774           $templateCache.put("uib/template/pager/pager.html",
40775             "<ul class=\"pager\">\n" +
40776             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40777             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40778             "</ul>\n" +
40779             "");
40780         }]);
40781
40782         angular.module("uib/template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
40783           $templateCache.put("uib/template/pagination/pager.html",
40784             "<ul class=\"pager\">\n" +
40785             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40786             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40787             "</ul>\n" +
40788             "");
40789         }]);
40790
40791         angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
40792           $templateCache.put("uib/template/pagination/pagination.html",
40793             "<ul class=\"pagination\">\n" +
40794             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
40795             "  <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" +
40796             "  <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" +
40797             "  <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" +
40798             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
40799             "</ul>\n" +
40800             "");
40801         }]);
40802
40803         angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
40804           $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
40805             "<div class=\"tooltip\"\n" +
40806             "  tooltip-animation-class=\"fade\"\n" +
40807             "  uib-tooltip-classes\n" +
40808             "  ng-class=\"{ in: isOpen() }\">\n" +
40809             "  <div class=\"tooltip-arrow\"></div>\n" +
40810             "  <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
40811             "</div>\n" +
40812             "");
40813         }]);
40814
40815         angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
40816           $templateCache.put("uib/template/tooltip/tooltip-popup.html",
40817             "<div class=\"tooltip\"\n" +
40818             "  tooltip-animation-class=\"fade\"\n" +
40819             "  uib-tooltip-classes\n" +
40820             "  ng-class=\"{ in: isOpen() }\">\n" +
40821             "  <div class=\"tooltip-arrow\"></div>\n" +
40822             "  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
40823             "</div>\n" +
40824             "");
40825         }]);
40826
40827         angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
40828           $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
40829             "<div class=\"tooltip\"\n" +
40830             "  tooltip-animation-class=\"fade\"\n" +
40831             "  uib-tooltip-classes\n" +
40832             "  ng-class=\"{ in: isOpen() }\">\n" +
40833             "  <div class=\"tooltip-arrow\"></div>\n" +
40834             "  <div class=\"tooltip-inner\"\n" +
40835             "    uib-tooltip-template-transclude=\"contentExp()\"\n" +
40836             "    tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40837             "</div>\n" +
40838             "");
40839         }]);
40840
40841         angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
40842           $templateCache.put("uib/template/popover/popover-html.html",
40843             "<div class=\"popover\"\n" +
40844             "  tooltip-animation-class=\"fade\"\n" +
40845             "  uib-tooltip-classes\n" +
40846             "  ng-class=\"{ in: isOpen() }\">\n" +
40847             "  <div class=\"arrow\"></div>\n" +
40848             "\n" +
40849             "  <div class=\"popover-inner\">\n" +
40850             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40851             "      <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
40852             "  </div>\n" +
40853             "</div>\n" +
40854             "");
40855         }]);
40856
40857         angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
40858           $templateCache.put("uib/template/popover/popover-template.html",
40859             "<div class=\"popover\"\n" +
40860             "  tooltip-animation-class=\"fade\"\n" +
40861             "  uib-tooltip-classes\n" +
40862             "  ng-class=\"{ in: isOpen() }\">\n" +
40863             "  <div class=\"arrow\"></div>\n" +
40864             "\n" +
40865             "  <div class=\"popover-inner\">\n" +
40866             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40867             "      <div class=\"popover-content\"\n" +
40868             "        uib-tooltip-template-transclude=\"contentExp()\"\n" +
40869             "        tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40870             "  </div>\n" +
40871             "</div>\n" +
40872             "");
40873         }]);
40874
40875         angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
40876           $templateCache.put("uib/template/popover/popover.html",
40877             "<div class=\"popover\"\n" +
40878             "  tooltip-animation-class=\"fade\"\n" +
40879             "  uib-tooltip-classes\n" +
40880             "  ng-class=\"{ in: isOpen() }\">\n" +
40881             "  <div class=\"arrow\"></div>\n" +
40882             "\n" +
40883             "  <div class=\"popover-inner\">\n" +
40884             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40885             "      <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
40886             "  </div>\n" +
40887             "</div>\n" +
40888             "");
40889         }]);
40890
40891         angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
40892           $templateCache.put("uib/template/progressbar/bar.html",
40893             "<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" +
40894             "");
40895         }]);
40896
40897         angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
40898           $templateCache.put("uib/template/progressbar/progress.html",
40899             "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
40900         }]);
40901
40902         angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
40903           $templateCache.put("uib/template/progressbar/progressbar.html",
40904             "<div class=\"progress\">\n" +
40905             "  <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" +
40906             "</div>\n" +
40907             "");
40908         }]);
40909
40910         angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
40911           $templateCache.put("uib/template/rating/rating.html",
40912             "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
40913             "    <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
40914             "    <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" +
40915             "</span>\n" +
40916             "");
40917         }]);
40918
40919         angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
40920           $templateCache.put("uib/template/tabs/tab.html",
40921             "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" +
40922             "  <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" +
40923             "</li>\n" +
40924             "");
40925         }]);
40926
40927         angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
40928           $templateCache.put("uib/template/tabs/tabset.html",
40929             "<div>\n" +
40930             "  <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
40931             "  <div class=\"tab-content\">\n" +
40932             "    <div class=\"tab-pane\" \n" +
40933             "         ng-repeat=\"tab in tabs\" \n" +
40934             "         ng-class=\"{active: tab.active}\"\n" +
40935             "         uib-tab-content-transclude=\"tab\">\n" +
40936             "    </div>\n" +
40937             "  </div>\n" +
40938             "</div>\n" +
40939             "");
40940         }]);
40941
40942         angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
40943           $templateCache.put("uib/template/timepicker/timepicker.html",
40944             "<table class=\"uib-timepicker\">\n" +
40945             "  <tbody>\n" +
40946             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40947             "      <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" +
40948             "      <td>&nbsp;</td>\n" +
40949             "      <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" +
40950             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40951             "      <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" +
40952             "      <td ng-show=\"showMeridian\"></td>\n" +
40953             "    </tr>\n" +
40954             "    <tr>\n" +
40955             "      <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
40956             "        <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" +
40957             "      </td>\n" +
40958             "      <td class=\"uib-separator\">:</td>\n" +
40959             "      <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
40960             "        <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" +
40961             "      </td>\n" +
40962             "      <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
40963             "      <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
40964             "        <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" +
40965             "      </td>\n" +
40966             "      <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" +
40967             "    </tr>\n" +
40968             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40969             "      <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" +
40970             "      <td>&nbsp;</td>\n" +
40971             "      <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" +
40972             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40973             "      <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" +
40974             "      <td ng-show=\"showMeridian\"></td>\n" +
40975             "    </tr>\n" +
40976             "  </tbody>\n" +
40977             "</table>\n" +
40978             "");
40979         }]);
40980
40981         angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
40982           $templateCache.put("uib/template/typeahead/typeahead-match.html",
40983             "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" +
40984             "");
40985         }]);
40986
40987         angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
40988           $templateCache.put("uib/template/typeahead/typeahead-popup.html",
40989             "<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" +
40990             "    <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" +
40991             "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
40992             "    </li>\n" +
40993             "</ul>\n" +
40994             "");
40995         }]);
40996         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>'); })
40997
40998 /***/ },
40999 /* 8 */
41000 /***/ function(module, exports) {
41001
41002         var app;
41003         (function (app) {
41004             var declares;
41005             (function (declares) {
41006                 var CommandInfo = (function () {
41007                     function CommandInfo(name) {
41008                         this.name = name;
41009                     }
41010                     return CommandInfo;
41011                 })();
41012                 declares.CommandInfo = CommandInfo;
41013             })(declares = app.declares || (app.declares = {}));
41014         })(app || (app = {}));
41015         var app;
41016         (function (app) {
41017             var services;
41018             (function (services) {
41019                 var APIEndPoint = (function () {
41020                     function APIEndPoint($resource, $http) {
41021                         this.$resource = $resource;
41022                         this.$http = $http;
41023                     }
41024                     APIEndPoint.prototype.resource = function (endPoint, data) {
41025                         var customAction = {
41026                             method: 'GET',
41027                             isArray: false
41028                         };
41029                         var execute = {
41030                             method: 'POST',
41031                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
41032                         };
41033                         return this.$resource(endPoint, {}, { execute: execute });
41034                     };
41035                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41036                         var endPoint = '/api/v1/optionControlFile/' + command;
41037                         return this.resource(endPoint, {}).get();
41038                     };
41039                     APIEndPoint.prototype.getFiles = function (fileId) {
41040                         var endPoint = '/api/v1/workspace';
41041                         if (fileId) {
41042                             endPoint += '/' + fileId;
41043                         }
41044                         return this.resource(endPoint, {}).get();
41045                     };
41046                     APIEndPoint.prototype.getDirectories = function () {
41047                         var endPoint = '/api/v1/all/workspace/directory';
41048                         return this.resource(endPoint, {}).get();
41049                     };
41050                     APIEndPoint.prototype.getTags = function () {
41051                         var endPoint = '/api/v1/tagList';
41052                         return this.resource(endPoint, {}).get();
41053                     };
41054                     APIEndPoint.prototype.getCommands = function () {
41055                         var endPoint = '/api/v1/commandList';
41056                         return this.resource(endPoint, {}).get();
41057                     };
41058                     APIEndPoint.prototype.execute = function (data) {
41059                         var endPoint = '/api/v1/execution';
41060                         var fd = new FormData();
41061                         fd.append('data', data);
41062                         return this.$http.post(endPoint, fd, {
41063                             headers: { 'Content-Type': undefined },
41064                             transformRequest: angular.identity
41065                         });
41066                     };
41067                     return APIEndPoint;
41068                 })();
41069                 services.APIEndPoint = APIEndPoint;
41070             })(services = app.services || (app.services = {}));
41071         })(app || (app = {}));
41072         var app;
41073         (function (app) {
41074             var services;
41075             (function (services) {
41076                 var MyModal = (function () {
41077                     function MyModal($uibModal) {
41078                         this.$uibModal = $uibModal;
41079                         this.modalOption = {
41080                             backdrop: true,
41081                             controller: null,
41082                             templateUrl: null,
41083                             size: null
41084                         };
41085                     }
41086                     MyModal.prototype.open = function (modalName) {
41087                         if (modalName === 'SelectCommand') {
41088                             this.modalOption.templateUrl = 'templates/select-command.html';
41089                             this.modalOption.size = 'lg';
41090                         }
41091                         return this.$uibModal.open(this.modalOption);
41092                     };
41093                     MyModal.prototype.selectCommand = function () {
41094                         this.modalOption.templateUrl = 'templates/select-command.html';
41095                         this.modalOption.controller = 'selectCommandController';
41096                         this.modalOption.controllerAs = 'c';
41097                         this.modalOption.size = 'lg';
41098                         return this.$uibModal.open(this.modalOption);
41099                     };
41100                     return MyModal;
41101                 })();
41102                 services.MyModal = MyModal;
41103             })(services = app.services || (app.services = {}));
41104         })(app || (app = {}));
41105         var app;
41106         (function (app) {
41107             var directives;
41108             (function (directives) {
41109                 var Command = (function () {
41110                     function Command() {
41111                         this.restrict = 'E';
41112                         this.replace = true;
41113                         this.scope = true;
41114                         this.controller = 'commandController';
41115                         this.controllerAs = 'ctrl';
41116                         this.bindToController = {
41117                             index: '=',
41118                             name: '=',
41119                             remove: '&',
41120                             list: '='
41121                         };
41122                         this.templateUrl = 'templates/command.html';
41123                     }
41124                     Command.Factory = function () {
41125                         var directive = function () {
41126                             return new Command();
41127                         };
41128                         directive.$inject = [];
41129                         return directive;
41130                     };
41131                     return Command;
41132                 })();
41133                 directives.Command = Command;
41134                 var CommandController = (function () {
41135                     function CommandController(APIEndPoint, $scope) {
41136                         this.APIEndPoint = APIEndPoint;
41137                         this.$scope = $scope;
41138                         var controller = this;
41139                         this.APIEndPoint
41140                             .getOptionControlFile('mrcImageNoiseAdd')
41141                             .$promise
41142                             .then(function (result) {
41143                             controller.options = result.info;
41144                         });
41145                         this.APIEndPoint
41146                             .getDirectories()
41147                             .$promise
41148                             .then(function (result) {
41149                             controller.dirs = result.info;
41150                         });
41151                         this.heading = "[" + this.index + "]: dcdFilePring";
41152                         this.isOpen = true;
41153                         this.$scope.$on('close', function () {
41154                             controller.isOpen = false;
41155                         });
41156                     }
41157                     CommandController.prototype.submit = function () {
41158                         var opt = [];
41159                         angular.forEach(this.options, function (option) {
41160                             var obj = {
41161                                 name: option.option,
41162                                 arguments: []
41163                             };
41164                             angular.forEach(option.arg, function (arg) {
41165                                 if (arg.input) {
41166                                     if (typeof arg.input === 'object') {
41167                                         obj.arguments.push(arg.input.name);
41168                                     }
41169                                     else {
41170                                         obj.arguments.push(arg.input);
41171                                     }
41172                                 }
41173                             });
41174                             if (obj.arguments.length > 0) {
41175                                 opt.push(obj);
41176                             }
41177                         });
41178                         var execObj = {
41179                             command: this.name,
41180                             workspace: this.workspace.fileId,
41181                             options: opt
41182                         };
41183                         this.APIEndPoint
41184                             .execute(JSON.stringify(execObj))
41185                             .then(function (result) {
41186                             console.log(result);
41187                         });
41188                     };
41189                     CommandController.prototype.removeMySelf = function (index) {
41190                         this.remove()(index, this.list);
41191                     };
41192                     CommandController.prototype.reloadFiles = function () {
41193                         var _this = this;
41194                         var fileId = this.workspace.fileId;
41195                         this.APIEndPoint
41196                             .getFiles(fileId)
41197                             .$promise
41198                             .then(function (result) {
41199                             var status = result.status;
41200                             if (status === 'success') {
41201                                 _this.files = result.info;
41202                             }
41203                             else {
41204                                 console.log(result.message);
41205                             }
41206                         });
41207                     };
41208                     CommandController.prototype.debug = function () {
41209                         console.log(this.files);
41210                         console.log(this.files);
41211                         console.log(this.workspace);
41212                     };
41213                     CommandController.$inject = ['APIEndPoint', '$scope'];
41214                     return CommandController;
41215                 })();
41216                 directives.CommandController = CommandController;
41217             })(directives = app.directives || (app.directives = {}));
41218         })(app || (app = {}));
41219         var app;
41220         (function (app) {
41221             var directives;
41222             (function (directives) {
41223                 var HeaderMenu = (function () {
41224                     function HeaderMenu() {
41225                         this.restrict = 'E';
41226                         this.replace = true;
41227                         this.templateUrl = 'templates/header-menu.html';
41228                     }
41229                     HeaderMenu.Factory = function () {
41230                         var directive = function () {
41231                             return new HeaderMenu();
41232                         };
41233                         return directive;
41234                     };
41235                     return HeaderMenu;
41236                 })();
41237                 directives.HeaderMenu = HeaderMenu;
41238             })(directives = app.directives || (app.directives = {}));
41239         })(app || (app = {}));
41240         var app;
41241         (function (app) {
41242             var directives;
41243             (function (directives) {
41244                 var Option = (function () {
41245                     function Option() {
41246                         this.restrict = 'E';
41247                         this.replace = true;
41248                         this.controller = 'optionController';
41249                         this.bindToController = {
41250                             info: '=',
41251                             files: '='
41252                         };
41253                         this.scope = true;
41254                         this.templateUrl = 'templates/option.html';
41255                         this.controllerAs = 'ctrl';
41256                     }
41257                     Option.Factory = function () {
41258                         var directive = function () {
41259                             return new Option();
41260                         };
41261                         directive.$inject = [];
41262                         return directive;
41263                     };
41264                     return Option;
41265                 })();
41266                 directives.Option = Option;
41267                 var OptionController = (function () {
41268                     function OptionController() {
41269                         var controller = this;
41270                         angular.forEach(controller.info.arg, function (arg) {
41271                             if (arg.initialValue) {
41272                                 if (arg.formType === 'number') {
41273                                     arg.input = parseInt(arg.initialValue);
41274                                 }
41275                                 else {
41276                                     arg.input = arg.initialValue;
41277                                 }
41278                             }
41279                         });
41280                     }
41281                     OptionController.$inject = [];
41282                     return OptionController;
41283                 })();
41284                 directives.OptionController = OptionController;
41285             })(directives = app.directives || (app.directives = {}));
41286         })(app || (app = {}));
41287         var app;
41288         (function (app) {
41289             var directives;
41290             (function (directives) {
41291                 var Directory = (function () {
41292                     function Directory() {
41293                         this.restrict = 'E';
41294                         this.replace = true;
41295                         this.controller = 'directoryController';
41296                         this.controllerAs = 'ctrl';
41297                         this.bindToController = {
41298                             info: '=',
41299                             add: '&',
41300                             list: '=',
41301                             files: '='
41302                         };
41303                         this.templateUrl = 'templates/directory.html';
41304                     }
41305                     Directory.Factory = function () {
41306                         var directive = function () {
41307                             return new Directory();
41308                         };
41309                         return directive;
41310                     };
41311                     return Directory;
41312                 })();
41313                 directives.Directory = Directory;
41314                 var DirectoryController = (function () {
41315                     function DirectoryController(APIEndPoint, $scope) {
41316                         this.APIEndPoint = APIEndPoint;
41317                         this.$scope = $scope;
41318                         var controller = this;
41319                         this.APIEndPoint
41320                             .getFiles(this.info.fileId)
41321                             .$promise
41322                             .then(function (result) {
41323                             if (result.status === 'success') {
41324                                 controller.files = result.info;
41325                                 angular.forEach(result.info, function (file) {
41326                                     if (file.fileType === '0') {
41327                                         var o = file;
41328                                         if (controller.info.path === '/') {
41329                                             o.path = '/' + file.name;
41330                                         }
41331                                         else {
41332                                             o.path = controller.info.path + '/' + file.name;
41333                                         }
41334                                         controller.add()(o, controller.list);
41335                                     }
41336                                 });
41337                             }
41338                             ;
41339                         });
41340                     }
41341                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41342                     return DirectoryController;
41343                 })();
41344                 directives.DirectoryController = DirectoryController;
41345             })(directives = app.directives || (app.directives = {}));
41346         })(app || (app = {}));
41347         var app;
41348         (function (app) {
41349             var controllers;
41350             (function (controllers) {
41351                 var Execution = (function () {
41352                     function Execution(MyModal, $scope) {
41353                         this.MyModal = MyModal;
41354                         this.$scope = $scope;
41355                         this.commandInfoList = [];
41356                     }
41357                     ;
41358                     Execution.prototype.add = function () {
41359                         this.$scope.$broadcast('close');
41360                         this.MyModal.selectCommand();
41361                     };
41362                     Execution.prototype.open = function () {
41363                         var result = this.MyModal.open('SelectCommand');
41364                         console.log(result);
41365                     };
41366                     Execution.prototype.remove = function (index, list) {
41367                         list.splice(index, 1);
41368                     };
41369                     Execution.prototype.close = function () {
41370                         console.log("close");
41371                     };
41372                     Execution.$inject = ['MyModal', '$scope'];
41373                     return Execution;
41374                 })();
41375                 controllers.Execution = Execution;
41376             })(controllers = app.controllers || (app.controllers = {}));
41377         })(app || (app = {}));
41378         var app;
41379         (function (app) {
41380             var controllers;
41381             (function (controllers) {
41382                 var Workspace = (function () {
41383                     function Workspace($scope, APIEndPoint) {
41384                         this.$scope = $scope;
41385                         this.APIEndPoint = APIEndPoint;
41386                         this.directoryList = [];
41387                         var controller = this;
41388                         var directoryList = this.directoryList;
41389                         var o = {
41390                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41391                             name: '',
41392                             parentId: '',
41393                             fileType: '',
41394                             createdAt: '',
41395                             updatedAt: '',
41396                             path: '/'
41397                         };
41398                         directoryList.push(o);
41399                     }
41400                     Workspace.prototype.addDirectory = function (info, directoryList) {
41401                         directoryList.push(info);
41402                     };
41403                     Workspace.$inject = ['$scope', 'APIEndPoint'];
41404                     return Workspace;
41405                 })();
41406                 controllers.Workspace = Workspace;
41407             })(controllers = app.controllers || (app.controllers = {}));
41408         })(app || (app = {}));
41409         var app;
41410         (function (app) {
41411             var controllers;
41412             (function (controllers) {
41413                 var History = (function () {
41414                     function History($scope) {
41415                         this.page = "History";
41416                     }
41417                     History.$inject = ['$scope'];
41418                     return History;
41419                 })();
41420                 controllers.History = History;
41421             })(controllers = app.controllers || (app.controllers = {}));
41422         })(app || (app = {}));
41423         var app;
41424         (function (app) {
41425             var controllers;
41426             (function (controllers) {
41427                 var SelectCommand = (function () {
41428                     function SelectCommand($scope, APIEndPoint) {
41429                         this.APIEndPoint = APIEndPoint;
41430                         var controller = this;
41431                         this.APIEndPoint
41432                             .getTags()
41433                             .$promise.then(function (result) {
41434                             controller.tags = result.info;
41435                         });
41436                         this.APIEndPoint
41437                             .getCommands()
41438                             .$promise.then(function (result) {
41439                             controller.commands = result.info;
41440                         });
41441                     }
41442                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
41443                     return SelectCommand;
41444                 })();
41445                 controllers.SelectCommand = SelectCommand;
41446             })(controllers = app.controllers || (app.controllers = {}));
41447         })(app || (app = {}));
41448         var app;
41449         (function (app) {
41450             'use strict';
41451             var appName = 'zephyr';
41452             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41453             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41454                 $urlRouterProvider.otherwise('/execution');
41455                 $locationProvider.html5Mode({
41456                     enabled: true,
41457                     requireBase: false
41458                 });
41459                 $stateProvider
41460                     .state('execution', {
41461                     url: '/execution',
41462                     templateUrl: 'templates/execution.html',
41463                     controller: 'executionController',
41464                     controllerAs: 'c'
41465                 })
41466                     .state('workspace', {
41467                     url: '/workspace',
41468                     templateUrl: 'templates/workspace.html',
41469                     controller: 'workspaceController',
41470                     controllerAs: 'c'
41471                 })
41472                     .state('history', {
41473                     url: '/history',
41474                     templateUrl: 'templates/history.html',
41475                     controller: 'historyController',
41476                     controllerAs: 'c'
41477                 });
41478             });
41479             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41480             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
41481             app.zephyr.service('MyModal', app.services.MyModal);
41482             app.zephyr.controller('executionController', app.controllers.Execution);
41483             app.zephyr.controller('workspaceController', app.controllers.Workspace);
41484             app.zephyr.controller('historyController', app.controllers.History);
41485             app.zephyr.controller('commandController', app.directives.CommandController);
41486             app.zephyr.controller('optionController', app.directives.OptionController);
41487             app.zephyr.controller('directoryController', app.directives.DirectoryController);
41488             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41489             app.zephyr.directive('command', app.directives.Command.Factory());
41490             app.zephyr.directive('option', app.directives.Option.Factory());
41491             app.zephyr.directive('directory', app.directives.Directory.Factory());
41492         })(app || (app = {}));
41493
41494
41495 /***/ },
41496 /* 9 */
41497 /***/ function(module, exports) {
41498
41499         var app;
41500         (function (app) {
41501             var declares;
41502             (function (declares) {
41503                 var CommandInfo = (function () {
41504                     function CommandInfo(name) {
41505                         this.name = name;
41506                     }
41507                     return CommandInfo;
41508                 })();
41509                 declares.CommandInfo = CommandInfo;
41510             })(declares = app.declares || (app.declares = {}));
41511         })(app || (app = {}));
41512         var app;
41513         (function (app) {
41514             var services;
41515             (function (services) {
41516                 var APIEndPoint = (function () {
41517                     function APIEndPoint($resource, $http) {
41518                         this.$resource = $resource;
41519                         this.$http = $http;
41520                     }
41521                     APIEndPoint.prototype.resource = function (endPoint, data) {
41522                         var customAction = {
41523                             method: 'GET',
41524                             isArray: false
41525                         };
41526                         var execute = {
41527                             method: 'POST',
41528                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
41529                         };
41530                         return this.$resource(endPoint, {}, { execute: execute });
41531                     };
41532                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41533                         var endPoint = '/api/v1/optionControlFile/' + command;
41534                         return this.resource(endPoint, {}).get();
41535                     };
41536                     APIEndPoint.prototype.getFiles = function (fileId) {
41537                         var endPoint = '/api/v1/workspace';
41538                         if (fileId) {
41539                             endPoint += '/' + fileId;
41540                         }
41541                         return this.resource(endPoint, {}).get();
41542                     };
41543                     APIEndPoint.prototype.getDirectories = function () {
41544                         var endPoint = '/api/v1/all/workspace/directory';
41545                         return this.resource(endPoint, {}).get();
41546                     };
41547                     APIEndPoint.prototype.getTags = function () {
41548                         var endPoint = '/api/v1/tagList';
41549                         return this.resource(endPoint, {}).get();
41550                     };
41551                     APIEndPoint.prototype.getCommands = function () {
41552                         var endPoint = '/api/v1/commandList';
41553                         return this.resource(endPoint, {}).get();
41554                     };
41555                     APIEndPoint.prototype.execute = function (data) {
41556                         var endPoint = '/api/v1/execution';
41557                         var fd = new FormData();
41558                         fd.append('data', data);
41559                         return this.$http.post(endPoint, fd, {
41560                             headers: { 'Content-Type': undefined },
41561                             transformRequest: angular.identity
41562                         });
41563                     };
41564                     return APIEndPoint;
41565                 })();
41566                 services.APIEndPoint = APIEndPoint;
41567             })(services = app.services || (app.services = {}));
41568         })(app || (app = {}));
41569         var app;
41570         (function (app) {
41571             var services;
41572             (function (services) {
41573                 var MyModal = (function () {
41574                     function MyModal($uibModal) {
41575                         this.$uibModal = $uibModal;
41576                         this.modalOption = {
41577                             backdrop: true,
41578                             controller: null,
41579                             templateUrl: null,
41580                             size: null
41581                         };
41582                     }
41583                     MyModal.prototype.open = function (modalName) {
41584                         if (modalName === 'SelectCommand') {
41585                             this.modalOption.templateUrl = 'templates/select-command.html';
41586                             this.modalOption.size = 'lg';
41587                         }
41588                         return this.$uibModal.open(this.modalOption);
41589                     };
41590                     MyModal.prototype.selectCommand = function () {
41591                         this.modalOption.templateUrl = 'templates/select-command.html';
41592                         this.modalOption.controller = 'selectCommandController';
41593                         this.modalOption.controllerAs = 'c';
41594                         this.modalOption.size = 'lg';
41595                         return this.$uibModal.open(this.modalOption);
41596                     };
41597                     return MyModal;
41598                 })();
41599                 services.MyModal = MyModal;
41600             })(services = app.services || (app.services = {}));
41601         })(app || (app = {}));
41602         var app;
41603         (function (app) {
41604             var directives;
41605             (function (directives) {
41606                 var Command = (function () {
41607                     function Command() {
41608                         this.restrict = 'E';
41609                         this.replace = true;
41610                         this.scope = true;
41611                         this.controller = 'commandController';
41612                         this.controllerAs = 'ctrl';
41613                         this.bindToController = {
41614                             index: '=',
41615                             name: '=',
41616                             remove: '&',
41617                             list: '='
41618                         };
41619                         this.templateUrl = 'templates/command.html';
41620                     }
41621                     Command.Factory = function () {
41622                         var directive = function () {
41623                             return new Command();
41624                         };
41625                         directive.$inject = [];
41626                         return directive;
41627                     };
41628                     return Command;
41629                 })();
41630                 directives.Command = Command;
41631                 var CommandController = (function () {
41632                     function CommandController(APIEndPoint, $scope) {
41633                         this.APIEndPoint = APIEndPoint;
41634                         this.$scope = $scope;
41635                         var controller = this;
41636                         this.APIEndPoint
41637                             .getOptionControlFile('mrcImageNoiseAdd')
41638                             .$promise
41639                             .then(function (result) {
41640                             controller.options = result.info;
41641                         });
41642                         this.APIEndPoint
41643                             .getDirectories()
41644                             .$promise
41645                             .then(function (result) {
41646                             controller.dirs = result.info;
41647                         });
41648                         this.heading = "[" + this.index + "]: dcdFilePring";
41649                         this.isOpen = true;
41650                         this.$scope.$on('close', function () {
41651                             controller.isOpen = false;
41652                         });
41653                     }
41654                     CommandController.prototype.submit = function () {
41655                         var opt = [];
41656                         angular.forEach(this.options, function (option) {
41657                             var obj = {
41658                                 name: option.option,
41659                                 arguments: []
41660                             };
41661                             angular.forEach(option.arg, function (arg) {
41662                                 if (arg.input) {
41663                                     if (typeof arg.input === 'object') {
41664                                         obj.arguments.push(arg.input.name);
41665                                     }
41666                                     else {
41667                                         obj.arguments.push(arg.input);
41668                                     }
41669                                 }
41670                             });
41671                             if (obj.arguments.length > 0) {
41672                                 opt.push(obj);
41673                             }
41674                         });
41675                         var execObj = {
41676                             command: this.name,
41677                             workspace: this.workspace.fileId,
41678                             options: opt
41679                         };
41680                         this.APIEndPoint
41681                             .execute(JSON.stringify(execObj))
41682                             .then(function (result) {
41683                             console.log(result);
41684                         });
41685                     };
41686                     CommandController.prototype.removeMySelf = function (index) {
41687                         this.remove()(index, this.list);
41688                     };
41689                     CommandController.prototype.reloadFiles = function () {
41690                         var _this = this;
41691                         var fileId = this.workspace.fileId;
41692                         this.APIEndPoint
41693                             .getFiles(fileId)
41694                             .$promise
41695                             .then(function (result) {
41696                             var status = result.status;
41697                             if (status === 'success') {
41698                                 _this.files = result.info;
41699                             }
41700                             else {
41701                                 console.log(result.message);
41702                             }
41703                         });
41704                     };
41705                     CommandController.prototype.debug = function () {
41706                         console.log(this.files);
41707                         console.log(this.files);
41708                         console.log(this.workspace);
41709                     };
41710                     CommandController.$inject = ['APIEndPoint', '$scope'];
41711                     return CommandController;
41712                 })();
41713                 directives.CommandController = CommandController;
41714             })(directives = app.directives || (app.directives = {}));
41715         })(app || (app = {}));
41716         var app;
41717         (function (app) {
41718             var directives;
41719             (function (directives) {
41720                 var HeaderMenu = (function () {
41721                     function HeaderMenu() {
41722                         this.restrict = 'E';
41723                         this.replace = true;
41724                         this.templateUrl = 'templates/header-menu.html';
41725                     }
41726                     HeaderMenu.Factory = function () {
41727                         var directive = function () {
41728                             return new HeaderMenu();
41729                         };
41730                         return directive;
41731                     };
41732                     return HeaderMenu;
41733                 })();
41734                 directives.HeaderMenu = HeaderMenu;
41735             })(directives = app.directives || (app.directives = {}));
41736         })(app || (app = {}));
41737         var app;
41738         (function (app) {
41739             var directives;
41740             (function (directives) {
41741                 var Option = (function () {
41742                     function Option() {
41743                         this.restrict = 'E';
41744                         this.replace = true;
41745                         this.controller = 'optionController';
41746                         this.bindToController = {
41747                             info: '=',
41748                             files: '='
41749                         };
41750                         this.scope = true;
41751                         this.templateUrl = 'templates/option.html';
41752                         this.controllerAs = 'ctrl';
41753                     }
41754                     Option.Factory = function () {
41755                         var directive = function () {
41756                             return new Option();
41757                         };
41758                         directive.$inject = [];
41759                         return directive;
41760                     };
41761                     return Option;
41762                 })();
41763                 directives.Option = Option;
41764                 var OptionController = (function () {
41765                     function OptionController() {
41766                         var controller = this;
41767                         angular.forEach(controller.info.arg, function (arg) {
41768                             if (arg.initialValue) {
41769                                 if (arg.formType === 'number') {
41770                                     arg.input = parseInt(arg.initialValue);
41771                                 }
41772                                 else {
41773                                     arg.input = arg.initialValue;
41774                                 }
41775                             }
41776                         });
41777                     }
41778                     OptionController.$inject = [];
41779                     return OptionController;
41780                 })();
41781                 directives.OptionController = OptionController;
41782             })(directives = app.directives || (app.directives = {}));
41783         })(app || (app = {}));
41784         var app;
41785         (function (app) {
41786             var directives;
41787             (function (directives) {
41788                 var Directory = (function () {
41789                     function Directory() {
41790                         this.restrict = 'E';
41791                         this.replace = true;
41792                         this.controller = 'directoryController';
41793                         this.controllerAs = 'ctrl';
41794                         this.bindToController = {
41795                             info: '=',
41796                             add: '&',
41797                             list: '=',
41798                             files: '='
41799                         };
41800                         this.templateUrl = 'templates/directory.html';
41801                     }
41802                     Directory.Factory = function () {
41803                         var directive = function () {
41804                             return new Directory();
41805                         };
41806                         return directive;
41807                     };
41808                     return Directory;
41809                 })();
41810                 directives.Directory = Directory;
41811                 var DirectoryController = (function () {
41812                     function DirectoryController(APIEndPoint, $scope) {
41813                         this.APIEndPoint = APIEndPoint;
41814                         this.$scope = $scope;
41815                         var controller = this;
41816                         this.APIEndPoint
41817                             .getFiles(this.info.fileId)
41818                             .$promise
41819                             .then(function (result) {
41820                             if (result.status === 'success') {
41821                                 controller.files = result.info;
41822                                 angular.forEach(result.info, function (file) {
41823                                     if (file.fileType === '0') {
41824                                         var o = file;
41825                                         if (controller.info.path === '/') {
41826                                             o.path = '/' + file.name;
41827                                         }
41828                                         else {
41829                                             o.path = controller.info.path + '/' + file.name;
41830                                         }
41831                                         controller.add()(o, controller.list);
41832                                     }
41833                                 });
41834                             }
41835                             ;
41836                         });
41837                     }
41838                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41839                     return DirectoryController;
41840                 })();
41841                 directives.DirectoryController = DirectoryController;
41842             })(directives = app.directives || (app.directives = {}));
41843         })(app || (app = {}));
41844         var app;
41845         (function (app) {
41846             var controllers;
41847             (function (controllers) {
41848                 var Execution = (function () {
41849                     function Execution(MyModal, $scope) {
41850                         this.MyModal = MyModal;
41851                         this.$scope = $scope;
41852                         this.commandInfoList = [];
41853                     }
41854                     ;
41855                     Execution.prototype.add = function () {
41856                         this.$scope.$broadcast('close');
41857                         this.MyModal.selectCommand();
41858                     };
41859                     Execution.prototype.open = function () {
41860                         var result = this.MyModal.open('SelectCommand');
41861                         console.log(result);
41862                     };
41863                     Execution.prototype.remove = function (index, list) {
41864                         list.splice(index, 1);
41865                     };
41866                     Execution.prototype.close = function () {
41867                         console.log("close");
41868                     };
41869                     Execution.$inject = ['MyModal', '$scope'];
41870                     return Execution;
41871                 })();
41872                 controllers.Execution = Execution;
41873             })(controllers = app.controllers || (app.controllers = {}));
41874         })(app || (app = {}));
41875         var app;
41876         (function (app) {
41877             var controllers;
41878             (function (controllers) {
41879                 var Workspace = (function () {
41880                     function Workspace($scope, APIEndPoint) {
41881                         this.$scope = $scope;
41882                         this.APIEndPoint = APIEndPoint;
41883                         this.directoryList = [];
41884                         var controller = this;
41885                         var directoryList = this.directoryList;
41886                         var o = {
41887                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41888                             name: '',
41889                             parentId: '',
41890                             fileType: '',
41891                             createdAt: '',
41892                             updatedAt: '',
41893                             path: '/'
41894                         };
41895                         directoryList.push(o);
41896                     }
41897                     Workspace.prototype.addDirectory = function (info, directoryList) {
41898                         directoryList.push(info);
41899                     };
41900                     Workspace.$inject = ['$scope', 'APIEndPoint'];
41901                     return Workspace;
41902                 })();
41903                 controllers.Workspace = Workspace;
41904             })(controllers = app.controllers || (app.controllers = {}));
41905         })(app || (app = {}));
41906         var app;
41907         (function (app) {
41908             var controllers;
41909             (function (controllers) {
41910                 var History = (function () {
41911                     function History($scope) {
41912                         this.page = "History";
41913                     }
41914                     History.$inject = ['$scope'];
41915                     return History;
41916                 })();
41917                 controllers.History = History;
41918             })(controllers = app.controllers || (app.controllers = {}));
41919         })(app || (app = {}));
41920         var app;
41921         (function (app) {
41922             var controllers;
41923             (function (controllers) {
41924                 var SelectCommand = (function () {
41925                     function SelectCommand($scope, APIEndPoint) {
41926                         this.APIEndPoint = APIEndPoint;
41927                         var controller = this;
41928                         this.APIEndPoint
41929                             .getTags()
41930                             .$promise.then(function (result) {
41931                             controller.tags = result.info;
41932                         });
41933                         this.APIEndPoint
41934                             .getCommands()
41935                             .$promise.then(function (result) {
41936                             controller.commands = result.info;
41937                         });
41938                     }
41939                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
41940                     return SelectCommand;
41941                 })();
41942                 controllers.SelectCommand = SelectCommand;
41943             })(controllers = app.controllers || (app.controllers = {}));
41944         })(app || (app = {}));
41945         var app;
41946         (function (app) {
41947             'use strict';
41948             var appName = 'zephyr';
41949             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41950             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41951                 $urlRouterProvider.otherwise('/execution');
41952                 $locationProvider.html5Mode({
41953                     enabled: true,
41954                     requireBase: false
41955                 });
41956                 $stateProvider
41957                     .state('execution', {
41958                     url: '/execution',
41959                     templateUrl: 'templates/execution.html',
41960                     controller: 'executionController',
41961                     controllerAs: 'c'
41962                 })
41963                     .state('workspace', {
41964                     url: '/workspace',
41965                     templateUrl: 'templates/workspace.html',
41966                     controller: 'workspaceController',
41967                     controllerAs: 'c'
41968                 })
41969                     .state('history', {
41970                     url: '/history',
41971                     templateUrl: 'templates/history.html',
41972                     controller: 'historyController',
41973                     controllerAs: 'c'
41974                 });
41975             });
41976             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41977             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
41978             app.zephyr.service('MyModal', app.services.MyModal);
41979             app.zephyr.controller('executionController', app.controllers.Execution);
41980             app.zephyr.controller('workspaceController', app.controllers.Workspace);
41981             app.zephyr.controller('historyController', app.controllers.History);
41982             app.zephyr.controller('commandController', app.directives.CommandController);
41983             app.zephyr.controller('optionController', app.directives.OptionController);
41984             app.zephyr.controller('directoryController', app.directives.DirectoryController);
41985             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41986             app.zephyr.directive('command', app.directives.Command.Factory());
41987             app.zephyr.directive('option', app.directives.Option.Factory());
41988             app.zephyr.directive('directory', app.directives.Directory.Factory());
41989         })(app || (app = {}));
41990
41991
41992 /***/ },
41993 /* 10 */
41994 /***/ function(module, exports) {
41995
41996         var app;
41997         (function (app) {
41998             var declares;
41999             (function (declares) {
42000                 var CommandInfo = (function () {
42001                     function CommandInfo(name) {
42002                         this.name = name;
42003                     }
42004                     return CommandInfo;
42005                 })();
42006                 declares.CommandInfo = CommandInfo;
42007             })(declares = app.declares || (app.declares = {}));
42008         })(app || (app = {}));
42009         var app;
42010         (function (app) {
42011             var services;
42012             (function (services) {
42013                 var APIEndPoint = (function () {
42014                     function APIEndPoint($resource, $http) {
42015                         this.$resource = $resource;
42016                         this.$http = $http;
42017                     }
42018                     APIEndPoint.prototype.resource = function (endPoint, data) {
42019                         var customAction = {
42020                             method: 'GET',
42021                             isArray: false
42022                         };
42023                         var execute = {
42024                             method: 'POST',
42025                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
42026                         };
42027                         return this.$resource(endPoint, {}, { execute: execute });
42028                     };
42029                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42030                         var endPoint = '/api/v1/optionControlFile/' + command;
42031                         return this.resource(endPoint, {}).get();
42032                     };
42033                     APIEndPoint.prototype.getFiles = function (fileId) {
42034                         var endPoint = '/api/v1/workspace';
42035                         if (fileId) {
42036                             endPoint += '/' + fileId;
42037                         }
42038                         return this.resource(endPoint, {}).get();
42039                     };
42040                     APIEndPoint.prototype.getDirectories = function () {
42041                         var endPoint = '/api/v1/all/workspace/directory';
42042                         return this.resource(endPoint, {}).get();
42043                     };
42044                     APIEndPoint.prototype.getTags = function () {
42045                         var endPoint = '/api/v1/tagList';
42046                         return this.resource(endPoint, {}).get();
42047                     };
42048                     APIEndPoint.prototype.getCommands = function () {
42049                         var endPoint = '/api/v1/commandList';
42050                         return this.resource(endPoint, {}).get();
42051                     };
42052                     APIEndPoint.prototype.execute = function (data) {
42053                         var endPoint = '/api/v1/execution';
42054                         var fd = new FormData();
42055                         fd.append('data', data);
42056                         return this.$http.post(endPoint, fd, {
42057                             headers: { 'Content-Type': undefined },
42058                             transformRequest: angular.identity
42059                         });
42060                     };
42061                     return APIEndPoint;
42062                 })();
42063                 services.APIEndPoint = APIEndPoint;
42064             })(services = app.services || (app.services = {}));
42065         })(app || (app = {}));
42066         var app;
42067         (function (app) {
42068             var services;
42069             (function (services) {
42070                 var MyModal = (function () {
42071                     function MyModal($uibModal) {
42072                         this.$uibModal = $uibModal;
42073                         this.modalOption = {
42074                             backdrop: true,
42075                             controller: null,
42076                             templateUrl: null,
42077                             size: null
42078                         };
42079                     }
42080                     MyModal.prototype.open = function (modalName) {
42081                         if (modalName === 'SelectCommand') {
42082                             this.modalOption.templateUrl = 'templates/select-command.html';
42083                             this.modalOption.size = 'lg';
42084                         }
42085                         return this.$uibModal.open(this.modalOption);
42086                     };
42087                     MyModal.prototype.selectCommand = function () {
42088                         this.modalOption.templateUrl = 'templates/select-command.html';
42089                         this.modalOption.controller = 'selectCommandController';
42090                         this.modalOption.controllerAs = 'c';
42091                         this.modalOption.size = 'lg';
42092                         return this.$uibModal.open(this.modalOption);
42093                     };
42094                     return MyModal;
42095                 })();
42096                 services.MyModal = MyModal;
42097             })(services = app.services || (app.services = {}));
42098         })(app || (app = {}));
42099         var app;
42100         (function (app) {
42101             var directives;
42102             (function (directives) {
42103                 var Command = (function () {
42104                     function Command() {
42105                         this.restrict = 'E';
42106                         this.replace = true;
42107                         this.scope = true;
42108                         this.controller = 'commandController';
42109                         this.controllerAs = 'ctrl';
42110                         this.bindToController = {
42111                             index: '=',
42112                             name: '=',
42113                             remove: '&',
42114                             list: '='
42115                         };
42116                         this.templateUrl = 'templates/command.html';
42117                     }
42118                     Command.Factory = function () {
42119                         var directive = function () {
42120                             return new Command();
42121                         };
42122                         directive.$inject = [];
42123                         return directive;
42124                     };
42125                     return Command;
42126                 })();
42127                 directives.Command = Command;
42128                 var CommandController = (function () {
42129                     function CommandController(APIEndPoint, $scope) {
42130                         this.APIEndPoint = APIEndPoint;
42131                         this.$scope = $scope;
42132                         var controller = this;
42133                         this.APIEndPoint
42134                             .getOptionControlFile('mrcImageNoiseAdd')
42135                             .$promise
42136                             .then(function (result) {
42137                             controller.options = result.info;
42138                         });
42139                         this.APIEndPoint
42140                             .getDirectories()
42141                             .$promise
42142                             .then(function (result) {
42143                             controller.dirs = result.info;
42144                         });
42145                         this.heading = "[" + this.index + "]: dcdFilePring";
42146                         this.isOpen = true;
42147                         this.$scope.$on('close', function () {
42148                             controller.isOpen = false;
42149                         });
42150                     }
42151                     CommandController.prototype.submit = function () {
42152                         var opt = [];
42153                         angular.forEach(this.options, function (option) {
42154                             var obj = {
42155                                 name: option.option,
42156                                 arguments: []
42157                             };
42158                             angular.forEach(option.arg, function (arg) {
42159                                 if (arg.input) {
42160                                     if (typeof arg.input === 'object') {
42161                                         obj.arguments.push(arg.input.name);
42162                                     }
42163                                     else {
42164                                         obj.arguments.push(arg.input);
42165                                     }
42166                                 }
42167                             });
42168                             if (obj.arguments.length > 0) {
42169                                 opt.push(obj);
42170                             }
42171                         });
42172                         var execObj = {
42173                             command: this.name,
42174                             workspace: this.workspace.fileId,
42175                             options: opt
42176                         };
42177                         this.APIEndPoint
42178                             .execute(JSON.stringify(execObj))
42179                             .then(function (result) {
42180                             console.log(result);
42181                         });
42182                     };
42183                     CommandController.prototype.removeMySelf = function (index) {
42184                         this.remove()(index, this.list);
42185                     };
42186                     CommandController.prototype.reloadFiles = function () {
42187                         var _this = this;
42188                         var fileId = this.workspace.fileId;
42189                         this.APIEndPoint
42190                             .getFiles(fileId)
42191                             .$promise
42192                             .then(function (result) {
42193                             var status = result.status;
42194                             if (status === 'success') {
42195                                 _this.files = result.info;
42196                             }
42197                             else {
42198                                 console.log(result.message);
42199                             }
42200                         });
42201                     };
42202                     CommandController.prototype.debug = function () {
42203                         console.log(this.files);
42204                         console.log(this.files);
42205                         console.log(this.workspace);
42206                     };
42207                     CommandController.$inject = ['APIEndPoint', '$scope'];
42208                     return CommandController;
42209                 })();
42210                 directives.CommandController = CommandController;
42211             })(directives = app.directives || (app.directives = {}));
42212         })(app || (app = {}));
42213         var app;
42214         (function (app) {
42215             var directives;
42216             (function (directives) {
42217                 var HeaderMenu = (function () {
42218                     function HeaderMenu() {
42219                         this.restrict = 'E';
42220                         this.replace = true;
42221                         this.templateUrl = 'templates/header-menu.html';
42222                     }
42223                     HeaderMenu.Factory = function () {
42224                         var directive = function () {
42225                             return new HeaderMenu();
42226                         };
42227                         return directive;
42228                     };
42229                     return HeaderMenu;
42230                 })();
42231                 directives.HeaderMenu = HeaderMenu;
42232             })(directives = app.directives || (app.directives = {}));
42233         })(app || (app = {}));
42234         var app;
42235         (function (app) {
42236             var directives;
42237             (function (directives) {
42238                 var Option = (function () {
42239                     function Option() {
42240                         this.restrict = 'E';
42241                         this.replace = true;
42242                         this.controller = 'optionController';
42243                         this.bindToController = {
42244                             info: '=',
42245                             files: '='
42246                         };
42247                         this.scope = true;
42248                         this.templateUrl = 'templates/option.html';
42249                         this.controllerAs = 'ctrl';
42250                     }
42251                     Option.Factory = function () {
42252                         var directive = function () {
42253                             return new Option();
42254                         };
42255                         directive.$inject = [];
42256                         return directive;
42257                     };
42258                     return Option;
42259                 })();
42260                 directives.Option = Option;
42261                 var OptionController = (function () {
42262                     function OptionController() {
42263                         var controller = this;
42264                         angular.forEach(controller.info.arg, function (arg) {
42265                             if (arg.initialValue) {
42266                                 if (arg.formType === 'number') {
42267                                     arg.input = parseInt(arg.initialValue);
42268                                 }
42269                                 else {
42270                                     arg.input = arg.initialValue;
42271                                 }
42272                             }
42273                         });
42274                     }
42275                     OptionController.$inject = [];
42276                     return OptionController;
42277                 })();
42278                 directives.OptionController = OptionController;
42279             })(directives = app.directives || (app.directives = {}));
42280         })(app || (app = {}));
42281         var app;
42282         (function (app) {
42283             var directives;
42284             (function (directives) {
42285                 var Directory = (function () {
42286                     function Directory() {
42287                         this.restrict = 'E';
42288                         this.replace = true;
42289                         this.controller = 'directoryController';
42290                         this.controllerAs = 'ctrl';
42291                         this.bindToController = {
42292                             info: '=',
42293                             add: '&',
42294                             list: '=',
42295                             files: '='
42296                         };
42297                         this.templateUrl = 'templates/directory.html';
42298                     }
42299                     Directory.Factory = function () {
42300                         var directive = function () {
42301                             return new Directory();
42302                         };
42303                         return directive;
42304                     };
42305                     return Directory;
42306                 })();
42307                 directives.Directory = Directory;
42308                 var DirectoryController = (function () {
42309                     function DirectoryController(APIEndPoint, $scope) {
42310                         this.APIEndPoint = APIEndPoint;
42311                         this.$scope = $scope;
42312                         var controller = this;
42313                         this.APIEndPoint
42314                             .getFiles(this.info.fileId)
42315                             .$promise
42316                             .then(function (result) {
42317                             if (result.status === 'success') {
42318                                 controller.files = result.info;
42319                                 angular.forEach(result.info, function (file) {
42320                                     if (file.fileType === '0') {
42321                                         var o = file;
42322                                         if (controller.info.path === '/') {
42323                                             o.path = '/' + file.name;
42324                                         }
42325                                         else {
42326                                             o.path = controller.info.path + '/' + file.name;
42327                                         }
42328                                         controller.add()(o, controller.list);
42329                                     }
42330                                 });
42331                             }
42332                             ;
42333                         });
42334                     }
42335                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
42336                     return DirectoryController;
42337                 })();
42338                 directives.DirectoryController = DirectoryController;
42339             })(directives = app.directives || (app.directives = {}));
42340         })(app || (app = {}));
42341         var app;
42342         (function (app) {
42343             var controllers;
42344             (function (controllers) {
42345                 var Execution = (function () {
42346                     function Execution(MyModal, $scope) {
42347                         this.MyModal = MyModal;
42348                         this.$scope = $scope;
42349                         this.commandInfoList = [];
42350                     }
42351                     ;
42352                     Execution.prototype.add = function () {
42353                         this.$scope.$broadcast('close');
42354                         this.MyModal.selectCommand();
42355                     };
42356                     Execution.prototype.open = function () {
42357                         var result = this.MyModal.open('SelectCommand');
42358                         console.log(result);
42359                     };
42360                     Execution.prototype.remove = function (index, list) {
42361                         list.splice(index, 1);
42362                     };
42363                     Execution.prototype.close = function () {
42364                         console.log("close");
42365                     };
42366                     Execution.$inject = ['MyModal', '$scope'];
42367                     return Execution;
42368                 })();
42369                 controllers.Execution = Execution;
42370             })(controllers = app.controllers || (app.controllers = {}));
42371         })(app || (app = {}));
42372         var app;
42373         (function (app) {
42374             var controllers;
42375             (function (controllers) {
42376                 var Workspace = (function () {
42377                     function Workspace($scope, APIEndPoint) {
42378                         this.$scope = $scope;
42379                         this.APIEndPoint = APIEndPoint;
42380                         this.directoryList = [];
42381                         var controller = this;
42382                         var directoryList = this.directoryList;
42383                         var o = {
42384                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
42385                             name: '',
42386                             parentId: '',
42387                             fileType: '',
42388                             createdAt: '',
42389                             updatedAt: '',
42390                             path: '/'
42391                         };
42392                         directoryList.push(o);
42393                     }
42394                     Workspace.prototype.addDirectory = function (info, directoryList) {
42395                         directoryList.push(info);
42396                     };
42397                     Workspace.$inject = ['$scope', 'APIEndPoint'];
42398                     return Workspace;
42399                 })();
42400                 controllers.Workspace = Workspace;
42401             })(controllers = app.controllers || (app.controllers = {}));
42402         })(app || (app = {}));
42403         var app;
42404         (function (app) {
42405             var controllers;
42406             (function (controllers) {
42407                 var History = (function () {
42408                     function History($scope) {
42409                         this.page = "History";
42410                     }
42411                     History.$inject = ['$scope'];
42412                     return History;
42413                 })();
42414                 controllers.History = History;
42415             })(controllers = app.controllers || (app.controllers = {}));
42416         })(app || (app = {}));
42417         var app;
42418         (function (app) {
42419             var controllers;
42420             (function (controllers) {
42421                 var SelectCommand = (function () {
42422                     function SelectCommand($scope, APIEndPoint) {
42423                         this.APIEndPoint = APIEndPoint;
42424                         var controller = this;
42425                         this.APIEndPoint
42426                             .getTags()
42427                             .$promise.then(function (result) {
42428                             controller.tags = result.info;
42429                         });
42430                         this.APIEndPoint
42431                             .getCommands()
42432                             .$promise.then(function (result) {
42433                             controller.commands = result.info;
42434                         });
42435                     }
42436                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
42437                     return SelectCommand;
42438                 })();
42439                 controllers.SelectCommand = SelectCommand;
42440             })(controllers = app.controllers || (app.controllers = {}));
42441         })(app || (app = {}));
42442         var app;
42443         (function (app) {
42444             'use strict';
42445             var appName = 'zephyr';
42446             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42447             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42448                 $urlRouterProvider.otherwise('/execution');
42449                 $locationProvider.html5Mode({
42450                     enabled: true,
42451                     requireBase: false
42452                 });
42453                 $stateProvider
42454                     .state('execution', {
42455                     url: '/execution',
42456                     templateUrl: 'templates/execution.html',
42457                     controller: 'executionController',
42458                     controllerAs: 'c'
42459                 })
42460                     .state('workspace', {
42461                     url: '/workspace',
42462                     templateUrl: 'templates/workspace.html',
42463                     controller: 'workspaceController',
42464                     controllerAs: 'c'
42465                 })
42466                     .state('history', {
42467                     url: '/history',
42468                     templateUrl: 'templates/history.html',
42469                     controller: 'historyController',
42470                     controllerAs: 'c'
42471                 });
42472             });
42473             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42474             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
42475             app.zephyr.service('MyModal', app.services.MyModal);
42476             app.zephyr.controller('executionController', app.controllers.Execution);
42477             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42478             app.zephyr.controller('historyController', app.controllers.History);
42479             app.zephyr.controller('commandController', app.directives.CommandController);
42480             app.zephyr.controller('optionController', app.directives.OptionController);
42481             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42482             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42483             app.zephyr.directive('command', app.directives.Command.Factory());
42484             app.zephyr.directive('option', app.directives.Option.Factory());
42485             app.zephyr.directive('directory', app.directives.Directory.Factory());
42486         })(app || (app = {}));
42487
42488
42489 /***/ },
42490 /* 11 */
42491 /***/ function(module, exports) {
42492
42493         var app;
42494         (function (app) {
42495             var declares;
42496             (function (declares) {
42497                 var CommandInfo = (function () {
42498                     function CommandInfo(name) {
42499                         this.name = name;
42500                     }
42501                     return CommandInfo;
42502                 })();
42503                 declares.CommandInfo = CommandInfo;
42504             })(declares = app.declares || (app.declares = {}));
42505         })(app || (app = {}));
42506         var app;
42507         (function (app) {
42508             var services;
42509             (function (services) {
42510                 var APIEndPoint = (function () {
42511                     function APIEndPoint($resource, $http) {
42512                         this.$resource = $resource;
42513                         this.$http = $http;
42514                     }
42515                     APIEndPoint.prototype.resource = function (endPoint, data) {
42516                         var customAction = {
42517                             method: 'GET',
42518                             isArray: false
42519                         };
42520                         var execute = {
42521                             method: 'POST',
42522                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
42523                         };
42524                         return this.$resource(endPoint, {}, { execute: execute });
42525                     };
42526                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42527                         var endPoint = '/api/v1/optionControlFile/' + command;
42528                         return this.resource(endPoint, {}).get();
42529                     };
42530                     APIEndPoint.prototype.getFiles = function (fileId) {
42531                         var endPoint = '/api/v1/workspace';
42532                         if (fileId) {
42533                             endPoint += '/' + fileId;
42534                         }
42535                         return this.resource(endPoint, {}).get();
42536                     };
42537                     APIEndPoint.prototype.getDirectories = function () {
42538                         var endPoint = '/api/v1/all/workspace/directory';
42539                         return this.resource(endPoint, {}).get();
42540                     };
42541                     APIEndPoint.prototype.getTags = function () {
42542                         var endPoint = '/api/v1/tagList';
42543                         return this.resource(endPoint, {}).get();
42544                     };
42545                     APIEndPoint.prototype.getCommands = function () {
42546                         var endPoint = '/api/v1/commandList';
42547                         return this.resource(endPoint, {}).get();
42548                     };
42549                     APIEndPoint.prototype.execute = function (data) {
42550                         var endPoint = '/api/v1/execution';
42551                         var fd = new FormData();
42552                         fd.append('data', data);
42553                         return this.$http.post(endPoint, fd, {
42554                             headers: { 'Content-Type': undefined },
42555                             transformRequest: angular.identity
42556                         });
42557                     };
42558                     return APIEndPoint;
42559                 })();
42560                 services.APIEndPoint = APIEndPoint;
42561             })(services = app.services || (app.services = {}));
42562         })(app || (app = {}));
42563         var app;
42564         (function (app) {
42565             var services;
42566             (function (services) {
42567                 var MyModal = (function () {
42568                     function MyModal($uibModal) {
42569                         this.$uibModal = $uibModal;
42570                         this.modalOption = {
42571                             backdrop: true,
42572                             controller: null,
42573                             templateUrl: null,
42574                             size: null
42575                         };
42576                     }
42577                     MyModal.prototype.open = function (modalName) {
42578                         if (modalName === 'SelectCommand') {
42579                             this.modalOption.templateUrl = 'templates/select-command.html';
42580                             this.modalOption.size = 'lg';
42581                         }
42582                         return this.$uibModal.open(this.modalOption);
42583                     };
42584                     MyModal.prototype.selectCommand = function () {
42585                         this.modalOption.templateUrl = 'templates/select-command.html';
42586                         this.modalOption.controller = 'selectCommandController';
42587                         this.modalOption.controllerAs = 'c';
42588                         this.modalOption.size = 'lg';
42589                         return this.$uibModal.open(this.modalOption);
42590                     };
42591                     return MyModal;
42592                 })();
42593                 services.MyModal = MyModal;
42594             })(services = app.services || (app.services = {}));
42595         })(app || (app = {}));
42596         var app;
42597         (function (app) {
42598             var directives;
42599             (function (directives) {
42600                 var Command = (function () {
42601                     function Command() {
42602                         this.restrict = 'E';
42603                         this.replace = true;
42604                         this.scope = true;
42605                         this.controller = 'commandController';
42606                         this.controllerAs = 'ctrl';
42607                         this.bindToController = {
42608                             index: '=',
42609                             name: '=',
42610                             remove: '&',
42611                             list: '='
42612                         };
42613                         this.templateUrl = 'templates/command.html';
42614                     }
42615                     Command.Factory = function () {
42616                         var directive = function () {
42617                             return new Command();
42618                         };
42619                         directive.$inject = [];
42620                         return directive;
42621                     };
42622                     return Command;
42623                 })();
42624                 directives.Command = Command;
42625                 var CommandController = (function () {
42626                     function CommandController(APIEndPoint, $scope) {
42627                         this.APIEndPoint = APIEndPoint;
42628                         this.$scope = $scope;
42629                         var controller = this;
42630                         this.APIEndPoint
42631                             .getOptionControlFile('mrcImageNoiseAdd')
42632                             .$promise
42633                             .then(function (result) {
42634                             controller.options = result.info;
42635                         });
42636                         this.APIEndPoint
42637                             .getDirectories()
42638                             .$promise
42639                             .then(function (result) {
42640                             controller.dirs = result.info;
42641                         });
42642                         this.heading = "[" + this.index + "]: dcdFilePring";
42643                         this.isOpen = true;
42644                         this.$scope.$on('close', function () {
42645                             controller.isOpen = false;
42646                         });
42647                     }
42648                     CommandController.prototype.submit = function () {
42649                         var opt = [];
42650                         angular.forEach(this.options, function (option) {
42651                             var obj = {
42652                                 name: option.option,
42653                                 arguments: []
42654                             };
42655                             angular.forEach(option.arg, function (arg) {
42656                                 if (arg.input) {
42657                                     if (typeof arg.input === 'object') {
42658                                         obj.arguments.push(arg.input.name);
42659                                     }
42660                                     else {
42661                                         obj.arguments.push(arg.input);
42662                                     }
42663                                 }
42664                             });
42665                             if (obj.arguments.length > 0) {
42666                                 opt.push(obj);
42667                             }
42668                         });
42669                         var execObj = {
42670                             command: this.name,
42671                             workspace: this.workspace.fileId,
42672                             options: opt
42673                         };
42674                         this.APIEndPoint
42675                             .execute(JSON.stringify(execObj))
42676                             .then(function (result) {
42677                             console.log(result);
42678                         });
42679                     };
42680                     CommandController.prototype.removeMySelf = function (index) {
42681                         this.remove()(index, this.list);
42682                     };
42683                     CommandController.prototype.reloadFiles = function () {
42684                         var _this = this;
42685                         var fileId = this.workspace.fileId;
42686                         this.APIEndPoint
42687                             .getFiles(fileId)
42688                             .$promise
42689                             .then(function (result) {
42690                             var status = result.status;
42691                             if (status === 'success') {
42692                                 _this.files = result.info;
42693                             }
42694                             else {
42695                                 console.log(result.message);
42696                             }
42697                         });
42698                     };
42699                     CommandController.prototype.debug = function () {
42700                         console.log(this.files);
42701                         console.log(this.files);
42702                         console.log(this.workspace);
42703                     };
42704                     CommandController.$inject = ['APIEndPoint', '$scope'];
42705                     return CommandController;
42706                 })();
42707                 directives.CommandController = CommandController;
42708             })(directives = app.directives || (app.directives = {}));
42709         })(app || (app = {}));
42710         var app;
42711         (function (app) {
42712             var directives;
42713             (function (directives) {
42714                 var HeaderMenu = (function () {
42715                     function HeaderMenu() {
42716                         this.restrict = 'E';
42717                         this.replace = true;
42718                         this.templateUrl = 'templates/header-menu.html';
42719                     }
42720                     HeaderMenu.Factory = function () {
42721                         var directive = function () {
42722                             return new HeaderMenu();
42723                         };
42724                         return directive;
42725                     };
42726                     return HeaderMenu;
42727                 })();
42728                 directives.HeaderMenu = HeaderMenu;
42729             })(directives = app.directives || (app.directives = {}));
42730         })(app || (app = {}));
42731         var app;
42732         (function (app) {
42733             var directives;
42734             (function (directives) {
42735                 var Option = (function () {
42736                     function Option() {
42737                         this.restrict = 'E';
42738                         this.replace = true;
42739                         this.controller = 'optionController';
42740                         this.bindToController = {
42741                             info: '=',
42742                             files: '='
42743                         };
42744                         this.scope = true;
42745                         this.templateUrl = 'templates/option.html';
42746                         this.controllerAs = 'ctrl';
42747                     }
42748                     Option.Factory = function () {
42749                         var directive = function () {
42750                             return new Option();
42751                         };
42752                         directive.$inject = [];
42753                         return directive;
42754                     };
42755                     return Option;
42756                 })();
42757                 directives.Option = Option;
42758                 var OptionController = (function () {
42759                     function OptionController() {
42760                         var controller = this;
42761                         angular.forEach(controller.info.arg, function (arg) {
42762                             if (arg.initialValue) {
42763                                 if (arg.formType === 'number') {
42764                                     arg.input = parseInt(arg.initialValue);
42765                                 }
42766                                 else {
42767                                     arg.input = arg.initialValue;
42768                                 }
42769                             }
42770                         });
42771                     }
42772                     OptionController.$inject = [];
42773                     return OptionController;
42774                 })();
42775                 directives.OptionController = OptionController;
42776             })(directives = app.directives || (app.directives = {}));
42777         })(app || (app = {}));
42778         var app;
42779         (function (app) {
42780             var directives;
42781             (function (directives) {
42782                 var Directory = (function () {
42783                     function Directory() {
42784                         this.restrict = 'E';
42785                         this.replace = true;
42786                         this.controller = 'directoryController';
42787                         this.controllerAs = 'ctrl';
42788                         this.bindToController = {
42789                             info: '=',
42790                             add: '&',
42791                             list: '=',
42792                             files: '='
42793                         };
42794                         this.templateUrl = 'templates/directory.html';
42795                     }
42796                     Directory.Factory = function () {
42797                         var directive = function () {
42798                             return new Directory();
42799                         };
42800                         return directive;
42801                     };
42802                     return Directory;
42803                 })();
42804                 directives.Directory = Directory;
42805                 var DirectoryController = (function () {
42806                     function DirectoryController(APIEndPoint, $scope) {
42807                         this.APIEndPoint = APIEndPoint;
42808                         this.$scope = $scope;
42809                         var controller = this;
42810                         this.APIEndPoint
42811                             .getFiles(this.info.fileId)
42812                             .$promise
42813                             .then(function (result) {
42814                             if (result.status === 'success') {
42815                                 controller.files = result.info;
42816                                 angular.forEach(result.info, function (file) {
42817                                     if (file.fileType === '0') {
42818                                         var o = file;
42819                                         if (controller.info.path === '/') {
42820                                             o.path = '/' + file.name;
42821                                         }
42822                                         else {
42823                                             o.path = controller.info.path + '/' + file.name;
42824                                         }
42825                                         controller.add()(o, controller.list);
42826                                     }
42827                                 });
42828                             }
42829                             ;
42830                         });
42831                     }
42832                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
42833                     return DirectoryController;
42834                 })();
42835                 directives.DirectoryController = DirectoryController;
42836             })(directives = app.directives || (app.directives = {}));
42837         })(app || (app = {}));
42838         var app;
42839         (function (app) {
42840             var controllers;
42841             (function (controllers) {
42842                 var Execution = (function () {
42843                     function Execution(MyModal, $scope) {
42844                         this.MyModal = MyModal;
42845                         this.$scope = $scope;
42846                         this.commandInfoList = [];
42847                     }
42848                     ;
42849                     Execution.prototype.add = function () {
42850                         this.$scope.$broadcast('close');
42851                         this.MyModal.selectCommand();
42852                     };
42853                     Execution.prototype.open = function () {
42854                         var result = this.MyModal.open('SelectCommand');
42855                         console.log(result);
42856                     };
42857                     Execution.prototype.remove = function (index, list) {
42858                         list.splice(index, 1);
42859                     };
42860                     Execution.prototype.close = function () {
42861                         console.log("close");
42862                     };
42863                     Execution.$inject = ['MyModal', '$scope'];
42864                     return Execution;
42865                 })();
42866                 controllers.Execution = Execution;
42867             })(controllers = app.controllers || (app.controllers = {}));
42868         })(app || (app = {}));
42869         var app;
42870         (function (app) {
42871             var controllers;
42872             (function (controllers) {
42873                 var Workspace = (function () {
42874                     function Workspace($scope, APIEndPoint) {
42875                         this.$scope = $scope;
42876                         this.APIEndPoint = APIEndPoint;
42877                         this.directoryList = [];
42878                         var controller = this;
42879                         var directoryList = this.directoryList;
42880                         var o = {
42881                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
42882                             name: '',
42883                             parentId: '',
42884                             fileType: '',
42885                             createdAt: '',
42886                             updatedAt: '',
42887                             path: '/'
42888                         };
42889                         directoryList.push(o);
42890                     }
42891                     Workspace.prototype.addDirectory = function (info, directoryList) {
42892                         directoryList.push(info);
42893                     };
42894                     Workspace.$inject = ['$scope', 'APIEndPoint'];
42895                     return Workspace;
42896                 })();
42897                 controllers.Workspace = Workspace;
42898             })(controllers = app.controllers || (app.controllers = {}));
42899         })(app || (app = {}));
42900         var app;
42901         (function (app) {
42902             var controllers;
42903             (function (controllers) {
42904                 var History = (function () {
42905                     function History($scope) {
42906                         this.page = "History";
42907                     }
42908                     History.$inject = ['$scope'];
42909                     return History;
42910                 })();
42911                 controllers.History = History;
42912             })(controllers = app.controllers || (app.controllers = {}));
42913         })(app || (app = {}));
42914         var app;
42915         (function (app) {
42916             var controllers;
42917             (function (controllers) {
42918                 var SelectCommand = (function () {
42919                     function SelectCommand($scope, APIEndPoint) {
42920                         this.APIEndPoint = APIEndPoint;
42921                         var controller = this;
42922                         this.APIEndPoint
42923                             .getTags()
42924                             .$promise.then(function (result) {
42925                             controller.tags = result.info;
42926                         });
42927                         this.APIEndPoint
42928                             .getCommands()
42929                             .$promise.then(function (result) {
42930                             controller.commands = result.info;
42931                         });
42932                     }
42933                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
42934                     return SelectCommand;
42935                 })();
42936                 controllers.SelectCommand = SelectCommand;
42937             })(controllers = app.controllers || (app.controllers = {}));
42938         })(app || (app = {}));
42939         var app;
42940         (function (app) {
42941             'use strict';
42942             var appName = 'zephyr';
42943             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42944             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42945                 $urlRouterProvider.otherwise('/execution');
42946                 $locationProvider.html5Mode({
42947                     enabled: true,
42948                     requireBase: false
42949                 });
42950                 $stateProvider
42951                     .state('execution', {
42952                     url: '/execution',
42953                     templateUrl: 'templates/execution.html',
42954                     controller: 'executionController',
42955                     controllerAs: 'c'
42956                 })
42957                     .state('workspace', {
42958                     url: '/workspace',
42959                     templateUrl: 'templates/workspace.html',
42960                     controller: 'workspaceController',
42961                     controllerAs: 'c'
42962                 })
42963                     .state('history', {
42964                     url: '/history',
42965                     templateUrl: 'templates/history.html',
42966                     controller: 'historyController',
42967                     controllerAs: 'c'
42968                 });
42969             });
42970             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42971             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
42972             app.zephyr.service('MyModal', app.services.MyModal);
42973             app.zephyr.controller('executionController', app.controllers.Execution);
42974             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42975             app.zephyr.controller('historyController', app.controllers.History);
42976             app.zephyr.controller('commandController', app.directives.CommandController);
42977             app.zephyr.controller('optionController', app.directives.OptionController);
42978             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42979             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42980             app.zephyr.directive('command', app.directives.Command.Factory());
42981             app.zephyr.directive('option', app.directives.Option.Factory());
42982             app.zephyr.directive('directory', app.directives.Directory.Factory());
42983         })(app || (app = {}));
42984
42985
42986 /***/ },
42987 /* 12 */
42988 /***/ function(module, exports) {
42989
42990         var app;
42991         (function (app) {
42992             var declares;
42993             (function (declares) {
42994                 var CommandInfo = (function () {
42995                     function CommandInfo(name) {
42996                         this.name = name;
42997                     }
42998                     return CommandInfo;
42999                 })();
43000                 declares.CommandInfo = CommandInfo;
43001             })(declares = app.declares || (app.declares = {}));
43002         })(app || (app = {}));
43003         var app;
43004         (function (app) {
43005             var services;
43006             (function (services) {
43007                 var APIEndPoint = (function () {
43008                     function APIEndPoint($resource, $http) {
43009                         this.$resource = $resource;
43010                         this.$http = $http;
43011                     }
43012                     APIEndPoint.prototype.resource = function (endPoint, data) {
43013                         var customAction = {
43014                             method: 'GET',
43015                             isArray: false
43016                         };
43017                         var execute = {
43018                             method: 'POST',
43019                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
43020                         };
43021                         return this.$resource(endPoint, {}, { execute: execute });
43022                     };
43023                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43024                         var endPoint = '/api/v1/optionControlFile/' + command;
43025                         return this.resource(endPoint, {}).get();
43026                     };
43027                     APIEndPoint.prototype.getFiles = function (fileId) {
43028                         var endPoint = '/api/v1/workspace';
43029                         if (fileId) {
43030                             endPoint += '/' + fileId;
43031                         }
43032                         return this.resource(endPoint, {}).get();
43033                     };
43034                     APIEndPoint.prototype.getDirectories = function () {
43035                         var endPoint = '/api/v1/all/workspace/directory';
43036                         return this.resource(endPoint, {}).get();
43037                     };
43038                     APIEndPoint.prototype.getTags = function () {
43039                         var endPoint = '/api/v1/tagList';
43040                         return this.resource(endPoint, {}).get();
43041                     };
43042                     APIEndPoint.prototype.getCommands = function () {
43043                         var endPoint = '/api/v1/commandList';
43044                         return this.resource(endPoint, {}).get();
43045                     };
43046                     APIEndPoint.prototype.execute = function (data) {
43047                         var endPoint = '/api/v1/execution';
43048                         var fd = new FormData();
43049                         fd.append('data', data);
43050                         return this.$http.post(endPoint, fd, {
43051                             headers: { 'Content-Type': undefined },
43052                             transformRequest: angular.identity
43053                         });
43054                     };
43055                     return APIEndPoint;
43056                 })();
43057                 services.APIEndPoint = APIEndPoint;
43058             })(services = app.services || (app.services = {}));
43059         })(app || (app = {}));
43060         var app;
43061         (function (app) {
43062             var services;
43063             (function (services) {
43064                 var MyModal = (function () {
43065                     function MyModal($uibModal) {
43066                         this.$uibModal = $uibModal;
43067                         this.modalOption = {
43068                             backdrop: true,
43069                             controller: null,
43070                             templateUrl: null,
43071                             size: null
43072                         };
43073                     }
43074                     MyModal.prototype.open = function (modalName) {
43075                         if (modalName === 'SelectCommand') {
43076                             this.modalOption.templateUrl = 'templates/select-command.html';
43077                             this.modalOption.size = 'lg';
43078                         }
43079                         return this.$uibModal.open(this.modalOption);
43080                     };
43081                     MyModal.prototype.selectCommand = function () {
43082                         this.modalOption.templateUrl = 'templates/select-command.html';
43083                         this.modalOption.controller = 'selectCommandController';
43084                         this.modalOption.controllerAs = 'c';
43085                         this.modalOption.size = 'lg';
43086                         return this.$uibModal.open(this.modalOption);
43087                     };
43088                     return MyModal;
43089                 })();
43090                 services.MyModal = MyModal;
43091             })(services = app.services || (app.services = {}));
43092         })(app || (app = {}));
43093         var app;
43094         (function (app) {
43095             var directives;
43096             (function (directives) {
43097                 var Command = (function () {
43098                     function Command() {
43099                         this.restrict = 'E';
43100                         this.replace = true;
43101                         this.scope = true;
43102                         this.controller = 'commandController';
43103                         this.controllerAs = 'ctrl';
43104                         this.bindToController = {
43105                             index: '=',
43106                             name: '=',
43107                             remove: '&',
43108                             list: '='
43109                         };
43110                         this.templateUrl = 'templates/command.html';
43111                     }
43112                     Command.Factory = function () {
43113                         var directive = function () {
43114                             return new Command();
43115                         };
43116                         directive.$inject = [];
43117                         return directive;
43118                     };
43119                     return Command;
43120                 })();
43121                 directives.Command = Command;
43122                 var CommandController = (function () {
43123                     function CommandController(APIEndPoint, $scope) {
43124                         this.APIEndPoint = APIEndPoint;
43125                         this.$scope = $scope;
43126                         var controller = this;
43127                         this.APIEndPoint
43128                             .getOptionControlFile('mrcImageNoiseAdd')
43129                             .$promise
43130                             .then(function (result) {
43131                             controller.options = result.info;
43132                         });
43133                         this.APIEndPoint
43134                             .getDirectories()
43135                             .$promise
43136                             .then(function (result) {
43137                             controller.dirs = result.info;
43138                         });
43139                         this.heading = "[" + this.index + "]: dcdFilePring";
43140                         this.isOpen = true;
43141                         this.$scope.$on('close', function () {
43142                             controller.isOpen = false;
43143                         });
43144                     }
43145                     CommandController.prototype.submit = function () {
43146                         var opt = [];
43147                         angular.forEach(this.options, function (option) {
43148                             var obj = {
43149                                 name: option.option,
43150                                 arguments: []
43151                             };
43152                             angular.forEach(option.arg, function (arg) {
43153                                 if (arg.input) {
43154                                     if (typeof arg.input === 'object') {
43155                                         obj.arguments.push(arg.input.name);
43156                                     }
43157                                     else {
43158                                         obj.arguments.push(arg.input);
43159                                     }
43160                                 }
43161                             });
43162                             if (obj.arguments.length > 0) {
43163                                 opt.push(obj);
43164                             }
43165                         });
43166                         var execObj = {
43167                             command: this.name,
43168                             workspace: this.workspace.fileId,
43169                             options: opt
43170                         };
43171                         this.APIEndPoint
43172                             .execute(JSON.stringify(execObj))
43173                             .then(function (result) {
43174                             console.log(result);
43175                         });
43176                     };
43177                     CommandController.prototype.removeMySelf = function (index) {
43178                         this.remove()(index, this.list);
43179                     };
43180                     CommandController.prototype.reloadFiles = function () {
43181                         var _this = this;
43182                         var fileId = this.workspace.fileId;
43183                         this.APIEndPoint
43184                             .getFiles(fileId)
43185                             .$promise
43186                             .then(function (result) {
43187                             var status = result.status;
43188                             if (status === 'success') {
43189                                 _this.files = result.info;
43190                             }
43191                             else {
43192                                 console.log(result.message);
43193                             }
43194                         });
43195                     };
43196                     CommandController.prototype.debug = function () {
43197                         console.log(this.files);
43198                         console.log(this.files);
43199                         console.log(this.workspace);
43200                     };
43201                     CommandController.$inject = ['APIEndPoint', '$scope'];
43202                     return CommandController;
43203                 })();
43204                 directives.CommandController = CommandController;
43205             })(directives = app.directives || (app.directives = {}));
43206         })(app || (app = {}));
43207         var app;
43208         (function (app) {
43209             var directives;
43210             (function (directives) {
43211                 var HeaderMenu = (function () {
43212                     function HeaderMenu() {
43213                         this.restrict = 'E';
43214                         this.replace = true;
43215                         this.templateUrl = 'templates/header-menu.html';
43216                     }
43217                     HeaderMenu.Factory = function () {
43218                         var directive = function () {
43219                             return new HeaderMenu();
43220                         };
43221                         return directive;
43222                     };
43223                     return HeaderMenu;
43224                 })();
43225                 directives.HeaderMenu = HeaderMenu;
43226             })(directives = app.directives || (app.directives = {}));
43227         })(app || (app = {}));
43228         var app;
43229         (function (app) {
43230             var directives;
43231             (function (directives) {
43232                 var Option = (function () {
43233                     function Option() {
43234                         this.restrict = 'E';
43235                         this.replace = true;
43236                         this.controller = 'optionController';
43237                         this.bindToController = {
43238                             info: '=',
43239                             files: '='
43240                         };
43241                         this.scope = true;
43242                         this.templateUrl = 'templates/option.html';
43243                         this.controllerAs = 'ctrl';
43244                     }
43245                     Option.Factory = function () {
43246                         var directive = function () {
43247                             return new Option();
43248                         };
43249                         directive.$inject = [];
43250                         return directive;
43251                     };
43252                     return Option;
43253                 })();
43254                 directives.Option = Option;
43255                 var OptionController = (function () {
43256                     function OptionController() {
43257                         var controller = this;
43258                         angular.forEach(controller.info.arg, function (arg) {
43259                             if (arg.initialValue) {
43260                                 if (arg.formType === 'number') {
43261                                     arg.input = parseInt(arg.initialValue);
43262                                 }
43263                                 else {
43264                                     arg.input = arg.initialValue;
43265                                 }
43266                             }
43267                         });
43268                     }
43269                     OptionController.$inject = [];
43270                     return OptionController;
43271                 })();
43272                 directives.OptionController = OptionController;
43273             })(directives = app.directives || (app.directives = {}));
43274         })(app || (app = {}));
43275         var app;
43276         (function (app) {
43277             var directives;
43278             (function (directives) {
43279                 var Directory = (function () {
43280                     function Directory() {
43281                         this.restrict = 'E';
43282                         this.replace = true;
43283                         this.controller = 'directoryController';
43284                         this.controllerAs = 'ctrl';
43285                         this.bindToController = {
43286                             info: '=',
43287                             add: '&',
43288                             list: '=',
43289                             files: '='
43290                         };
43291                         this.templateUrl = 'templates/directory.html';
43292                     }
43293                     Directory.Factory = function () {
43294                         var directive = function () {
43295                             return new Directory();
43296                         };
43297                         return directive;
43298                     };
43299                     return Directory;
43300                 })();
43301                 directives.Directory = Directory;
43302                 var DirectoryController = (function () {
43303                     function DirectoryController(APIEndPoint, $scope) {
43304                         this.APIEndPoint = APIEndPoint;
43305                         this.$scope = $scope;
43306                         var controller = this;
43307                         this.APIEndPoint
43308                             .getFiles(this.info.fileId)
43309                             .$promise
43310                             .then(function (result) {
43311                             if (result.status === 'success') {
43312                                 controller.files = result.info;
43313                                 angular.forEach(result.info, function (file) {
43314                                     if (file.fileType === '0') {
43315                                         var o = file;
43316                                         if (controller.info.path === '/') {
43317                                             o.path = '/' + file.name;
43318                                         }
43319                                         else {
43320                                             o.path = controller.info.path + '/' + file.name;
43321                                         }
43322                                         controller.add()(o, controller.list);
43323                                     }
43324                                 });
43325                             }
43326                             ;
43327                         });
43328                     }
43329                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43330                     return DirectoryController;
43331                 })();
43332                 directives.DirectoryController = DirectoryController;
43333             })(directives = app.directives || (app.directives = {}));
43334         })(app || (app = {}));
43335         var app;
43336         (function (app) {
43337             var controllers;
43338             (function (controllers) {
43339                 var Execution = (function () {
43340                     function Execution(MyModal, $scope) {
43341                         this.MyModal = MyModal;
43342                         this.$scope = $scope;
43343                         this.commandInfoList = [];
43344                     }
43345                     ;
43346                     Execution.prototype.add = function () {
43347                         this.$scope.$broadcast('close');
43348                         this.MyModal.selectCommand();
43349                     };
43350                     Execution.prototype.open = function () {
43351                         var result = this.MyModal.open('SelectCommand');
43352                         console.log(result);
43353                     };
43354                     Execution.prototype.remove = function (index, list) {
43355                         list.splice(index, 1);
43356                     };
43357                     Execution.prototype.close = function () {
43358                         console.log("close");
43359                     };
43360                     Execution.$inject = ['MyModal', '$scope'];
43361                     return Execution;
43362                 })();
43363                 controllers.Execution = Execution;
43364             })(controllers = app.controllers || (app.controllers = {}));
43365         })(app || (app = {}));
43366         var app;
43367         (function (app) {
43368             var controllers;
43369             (function (controllers) {
43370                 var Workspace = (function () {
43371                     function Workspace($scope, APIEndPoint) {
43372                         this.$scope = $scope;
43373                         this.APIEndPoint = APIEndPoint;
43374                         this.directoryList = [];
43375                         var controller = this;
43376                         var directoryList = this.directoryList;
43377                         var o = {
43378                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43379                             name: '',
43380                             parentId: '',
43381                             fileType: '',
43382                             createdAt: '',
43383                             updatedAt: '',
43384                             path: '/'
43385                         };
43386                         directoryList.push(o);
43387                     }
43388                     Workspace.prototype.addDirectory = function (info, directoryList) {
43389                         directoryList.push(info);
43390                     };
43391                     Workspace.$inject = ['$scope', 'APIEndPoint'];
43392                     return Workspace;
43393                 })();
43394                 controllers.Workspace = Workspace;
43395             })(controllers = app.controllers || (app.controllers = {}));
43396         })(app || (app = {}));
43397         var app;
43398         (function (app) {
43399             var controllers;
43400             (function (controllers) {
43401                 var History = (function () {
43402                     function History($scope) {
43403                         this.page = "History";
43404                     }
43405                     History.$inject = ['$scope'];
43406                     return History;
43407                 })();
43408                 controllers.History = History;
43409             })(controllers = app.controllers || (app.controllers = {}));
43410         })(app || (app = {}));
43411         var app;
43412         (function (app) {
43413             var controllers;
43414             (function (controllers) {
43415                 var SelectCommand = (function () {
43416                     function SelectCommand($scope, APIEndPoint) {
43417                         this.APIEndPoint = APIEndPoint;
43418                         var controller = this;
43419                         this.APIEndPoint
43420                             .getTags()
43421                             .$promise.then(function (result) {
43422                             controller.tags = result.info;
43423                         });
43424                         this.APIEndPoint
43425                             .getCommands()
43426                             .$promise.then(function (result) {
43427                             controller.commands = result.info;
43428                         });
43429                     }
43430                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
43431                     return SelectCommand;
43432                 })();
43433                 controllers.SelectCommand = SelectCommand;
43434             })(controllers = app.controllers || (app.controllers = {}));
43435         })(app || (app = {}));
43436         var app;
43437         (function (app) {
43438             'use strict';
43439             var appName = 'zephyr';
43440             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43441             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43442                 $urlRouterProvider.otherwise('/execution');
43443                 $locationProvider.html5Mode({
43444                     enabled: true,
43445                     requireBase: false
43446                 });
43447                 $stateProvider
43448                     .state('execution', {
43449                     url: '/execution',
43450                     templateUrl: 'templates/execution.html',
43451                     controller: 'executionController',
43452                     controllerAs: 'c'
43453                 })
43454                     .state('workspace', {
43455                     url: '/workspace',
43456                     templateUrl: 'templates/workspace.html',
43457                     controller: 'workspaceController',
43458                     controllerAs: 'c'
43459                 })
43460                     .state('history', {
43461                     url: '/history',
43462                     templateUrl: 'templates/history.html',
43463                     controller: 'historyController',
43464                     controllerAs: 'c'
43465                 });
43466             });
43467             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43468             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
43469             app.zephyr.service('MyModal', app.services.MyModal);
43470             app.zephyr.controller('executionController', app.controllers.Execution);
43471             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43472             app.zephyr.controller('historyController', app.controllers.History);
43473             app.zephyr.controller('commandController', app.directives.CommandController);
43474             app.zephyr.controller('optionController', app.directives.OptionController);
43475             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43476             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43477             app.zephyr.directive('command', app.directives.Command.Factory());
43478             app.zephyr.directive('option', app.directives.Option.Factory());
43479             app.zephyr.directive('directory', app.directives.Directory.Factory());
43480         })(app || (app = {}));
43481
43482
43483 /***/ },
43484 /* 13 */
43485 /***/ function(module, exports) {
43486
43487         var app;
43488         (function (app) {
43489             var declares;
43490             (function (declares) {
43491                 var CommandInfo = (function () {
43492                     function CommandInfo(name) {
43493                         this.name = name;
43494                     }
43495                     return CommandInfo;
43496                 })();
43497                 declares.CommandInfo = CommandInfo;
43498             })(declares = app.declares || (app.declares = {}));
43499         })(app || (app = {}));
43500         var app;
43501         (function (app) {
43502             var services;
43503             (function (services) {
43504                 var APIEndPoint = (function () {
43505                     function APIEndPoint($resource, $http) {
43506                         this.$resource = $resource;
43507                         this.$http = $http;
43508                     }
43509                     APIEndPoint.prototype.resource = function (endPoint, data) {
43510                         var customAction = {
43511                             method: 'GET',
43512                             isArray: false
43513                         };
43514                         var execute = {
43515                             method: 'POST',
43516                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
43517                         };
43518                         return this.$resource(endPoint, {}, { execute: execute });
43519                     };
43520                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43521                         var endPoint = '/api/v1/optionControlFile/' + command;
43522                         return this.resource(endPoint, {}).get();
43523                     };
43524                     APIEndPoint.prototype.getFiles = function (fileId) {
43525                         var endPoint = '/api/v1/workspace';
43526                         if (fileId) {
43527                             endPoint += '/' + fileId;
43528                         }
43529                         return this.resource(endPoint, {}).get();
43530                     };
43531                     APIEndPoint.prototype.getDirectories = function () {
43532                         var endPoint = '/api/v1/all/workspace/directory';
43533                         return this.resource(endPoint, {}).get();
43534                     };
43535                     APIEndPoint.prototype.getTags = function () {
43536                         var endPoint = '/api/v1/tagList';
43537                         return this.resource(endPoint, {}).get();
43538                     };
43539                     APIEndPoint.prototype.getCommands = function () {
43540                         var endPoint = '/api/v1/commandList';
43541                         return this.resource(endPoint, {}).get();
43542                     };
43543                     APIEndPoint.prototype.execute = function (data) {
43544                         var endPoint = '/api/v1/execution';
43545                         var fd = new FormData();
43546                         fd.append('data', data);
43547                         return this.$http.post(endPoint, fd, {
43548                             headers: { 'Content-Type': undefined },
43549                             transformRequest: angular.identity
43550                         });
43551                     };
43552                     return APIEndPoint;
43553                 })();
43554                 services.APIEndPoint = APIEndPoint;
43555             })(services = app.services || (app.services = {}));
43556         })(app || (app = {}));
43557         var app;
43558         (function (app) {
43559             var services;
43560             (function (services) {
43561                 var MyModal = (function () {
43562                     function MyModal($uibModal) {
43563                         this.$uibModal = $uibModal;
43564                         this.modalOption = {
43565                             backdrop: true,
43566                             controller: null,
43567                             templateUrl: null,
43568                             size: null
43569                         };
43570                     }
43571                     MyModal.prototype.open = function (modalName) {
43572                         if (modalName === 'SelectCommand') {
43573                             this.modalOption.templateUrl = 'templates/select-command.html';
43574                             this.modalOption.size = 'lg';
43575                         }
43576                         return this.$uibModal.open(this.modalOption);
43577                     };
43578                     MyModal.prototype.selectCommand = function () {
43579                         this.modalOption.templateUrl = 'templates/select-command.html';
43580                         this.modalOption.controller = 'selectCommandController';
43581                         this.modalOption.controllerAs = 'c';
43582                         this.modalOption.size = 'lg';
43583                         return this.$uibModal.open(this.modalOption);
43584                     };
43585                     return MyModal;
43586                 })();
43587                 services.MyModal = MyModal;
43588             })(services = app.services || (app.services = {}));
43589         })(app || (app = {}));
43590         var app;
43591         (function (app) {
43592             var directives;
43593             (function (directives) {
43594                 var Command = (function () {
43595                     function Command() {
43596                         this.restrict = 'E';
43597                         this.replace = true;
43598                         this.scope = true;
43599                         this.controller = 'commandController';
43600                         this.controllerAs = 'ctrl';
43601                         this.bindToController = {
43602                             index: '=',
43603                             name: '=',
43604                             remove: '&',
43605                             list: '='
43606                         };
43607                         this.templateUrl = 'templates/command.html';
43608                     }
43609                     Command.Factory = function () {
43610                         var directive = function () {
43611                             return new Command();
43612                         };
43613                         directive.$inject = [];
43614                         return directive;
43615                     };
43616                     return Command;
43617                 })();
43618                 directives.Command = Command;
43619                 var CommandController = (function () {
43620                     function CommandController(APIEndPoint, $scope) {
43621                         this.APIEndPoint = APIEndPoint;
43622                         this.$scope = $scope;
43623                         var controller = this;
43624                         this.APIEndPoint
43625                             .getOptionControlFile('mrcImageNoiseAdd')
43626                             .$promise
43627                             .then(function (result) {
43628                             controller.options = result.info;
43629                         });
43630                         this.APIEndPoint
43631                             .getDirectories()
43632                             .$promise
43633                             .then(function (result) {
43634                             controller.dirs = result.info;
43635                         });
43636                         this.heading = "[" + this.index + "]: dcdFilePring";
43637                         this.isOpen = true;
43638                         this.$scope.$on('close', function () {
43639                             controller.isOpen = false;
43640                         });
43641                     }
43642                     CommandController.prototype.submit = function () {
43643                         var opt = [];
43644                         angular.forEach(this.options, function (option) {
43645                             var obj = {
43646                                 name: option.option,
43647                                 arguments: []
43648                             };
43649                             angular.forEach(option.arg, function (arg) {
43650                                 if (arg.input) {
43651                                     if (typeof arg.input === 'object') {
43652                                         obj.arguments.push(arg.input.name);
43653                                     }
43654                                     else {
43655                                         obj.arguments.push(arg.input);
43656                                     }
43657                                 }
43658                             });
43659                             if (obj.arguments.length > 0) {
43660                                 opt.push(obj);
43661                             }
43662                         });
43663                         var execObj = {
43664                             command: this.name,
43665                             workspace: this.workspace.fileId,
43666                             options: opt
43667                         };
43668                         this.APIEndPoint
43669                             .execute(JSON.stringify(execObj))
43670                             .then(function (result) {
43671                             console.log(result);
43672                         });
43673                     };
43674                     CommandController.prototype.removeMySelf = function (index) {
43675                         this.remove()(index, this.list);
43676                     };
43677                     CommandController.prototype.reloadFiles = function () {
43678                         var _this = this;
43679                         var fileId = this.workspace.fileId;
43680                         this.APIEndPoint
43681                             .getFiles(fileId)
43682                             .$promise
43683                             .then(function (result) {
43684                             var status = result.status;
43685                             if (status === 'success') {
43686                                 _this.files = result.info;
43687                             }
43688                             else {
43689                                 console.log(result.message);
43690                             }
43691                         });
43692                     };
43693                     CommandController.prototype.debug = function () {
43694                         console.log(this.files);
43695                         console.log(this.files);
43696                         console.log(this.workspace);
43697                     };
43698                     CommandController.$inject = ['APIEndPoint', '$scope'];
43699                     return CommandController;
43700                 })();
43701                 directives.CommandController = CommandController;
43702             })(directives = app.directives || (app.directives = {}));
43703         })(app || (app = {}));
43704         var app;
43705         (function (app) {
43706             var directives;
43707             (function (directives) {
43708                 var HeaderMenu = (function () {
43709                     function HeaderMenu() {
43710                         this.restrict = 'E';
43711                         this.replace = true;
43712                         this.templateUrl = 'templates/header-menu.html';
43713                     }
43714                     HeaderMenu.Factory = function () {
43715                         var directive = function () {
43716                             return new HeaderMenu();
43717                         };
43718                         return directive;
43719                     };
43720                     return HeaderMenu;
43721                 })();
43722                 directives.HeaderMenu = HeaderMenu;
43723             })(directives = app.directives || (app.directives = {}));
43724         })(app || (app = {}));
43725         var app;
43726         (function (app) {
43727             var directives;
43728             (function (directives) {
43729                 var Option = (function () {
43730                     function Option() {
43731                         this.restrict = 'E';
43732                         this.replace = true;
43733                         this.controller = 'optionController';
43734                         this.bindToController = {
43735                             info: '=',
43736                             files: '='
43737                         };
43738                         this.scope = true;
43739                         this.templateUrl = 'templates/option.html';
43740                         this.controllerAs = 'ctrl';
43741                     }
43742                     Option.Factory = function () {
43743                         var directive = function () {
43744                             return new Option();
43745                         };
43746                         directive.$inject = [];
43747                         return directive;
43748                     };
43749                     return Option;
43750                 })();
43751                 directives.Option = Option;
43752                 var OptionController = (function () {
43753                     function OptionController() {
43754                         var controller = this;
43755                         angular.forEach(controller.info.arg, function (arg) {
43756                             if (arg.initialValue) {
43757                                 if (arg.formType === 'number') {
43758                                     arg.input = parseInt(arg.initialValue);
43759                                 }
43760                                 else {
43761                                     arg.input = arg.initialValue;
43762                                 }
43763                             }
43764                         });
43765                     }
43766                     OptionController.$inject = [];
43767                     return OptionController;
43768                 })();
43769                 directives.OptionController = OptionController;
43770             })(directives = app.directives || (app.directives = {}));
43771         })(app || (app = {}));
43772         var app;
43773         (function (app) {
43774             var directives;
43775             (function (directives) {
43776                 var Directory = (function () {
43777                     function Directory() {
43778                         this.restrict = 'E';
43779                         this.replace = true;
43780                         this.controller = 'directoryController';
43781                         this.controllerAs = 'ctrl';
43782                         this.bindToController = {
43783                             info: '=',
43784                             add: '&',
43785                             list: '=',
43786                             files: '='
43787                         };
43788                         this.templateUrl = 'templates/directory.html';
43789                     }
43790                     Directory.Factory = function () {
43791                         var directive = function () {
43792                             return new Directory();
43793                         };
43794                         return directive;
43795                     };
43796                     return Directory;
43797                 })();
43798                 directives.Directory = Directory;
43799                 var DirectoryController = (function () {
43800                     function DirectoryController(APIEndPoint, $scope) {
43801                         this.APIEndPoint = APIEndPoint;
43802                         this.$scope = $scope;
43803                         var controller = this;
43804                         this.APIEndPoint
43805                             .getFiles(this.info.fileId)
43806                             .$promise
43807                             .then(function (result) {
43808                             if (result.status === 'success') {
43809                                 controller.files = result.info;
43810                                 angular.forEach(result.info, function (file) {
43811                                     if (file.fileType === '0') {
43812                                         var o = file;
43813                                         if (controller.info.path === '/') {
43814                                             o.path = '/' + file.name;
43815                                         }
43816                                         else {
43817                                             o.path = controller.info.path + '/' + file.name;
43818                                         }
43819                                         controller.add()(o, controller.list);
43820                                     }
43821                                 });
43822                             }
43823                             ;
43824                         });
43825                     }
43826                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43827                     return DirectoryController;
43828                 })();
43829                 directives.DirectoryController = DirectoryController;
43830             })(directives = app.directives || (app.directives = {}));
43831         })(app || (app = {}));
43832         var app;
43833         (function (app) {
43834             var controllers;
43835             (function (controllers) {
43836                 var Execution = (function () {
43837                     function Execution(MyModal, $scope) {
43838                         this.MyModal = MyModal;
43839                         this.$scope = $scope;
43840                         this.commandInfoList = [];
43841                     }
43842                     ;
43843                     Execution.prototype.add = function () {
43844                         this.$scope.$broadcast('close');
43845                         this.MyModal.selectCommand();
43846                     };
43847                     Execution.prototype.open = function () {
43848                         var result = this.MyModal.open('SelectCommand');
43849                         console.log(result);
43850                     };
43851                     Execution.prototype.remove = function (index, list) {
43852                         list.splice(index, 1);
43853                     };
43854                     Execution.prototype.close = function () {
43855                         console.log("close");
43856                     };
43857                     Execution.$inject = ['MyModal', '$scope'];
43858                     return Execution;
43859                 })();
43860                 controllers.Execution = Execution;
43861             })(controllers = app.controllers || (app.controllers = {}));
43862         })(app || (app = {}));
43863         var app;
43864         (function (app) {
43865             var controllers;
43866             (function (controllers) {
43867                 var Workspace = (function () {
43868                     function Workspace($scope, APIEndPoint) {
43869                         this.$scope = $scope;
43870                         this.APIEndPoint = APIEndPoint;
43871                         this.directoryList = [];
43872                         var controller = this;
43873                         var directoryList = this.directoryList;
43874                         var o = {
43875                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43876                             name: '',
43877                             parentId: '',
43878                             fileType: '',
43879                             createdAt: '',
43880                             updatedAt: '',
43881                             path: '/'
43882                         };
43883                         directoryList.push(o);
43884                     }
43885                     Workspace.prototype.addDirectory = function (info, directoryList) {
43886                         directoryList.push(info);
43887                     };
43888                     Workspace.$inject = ['$scope', 'APIEndPoint'];
43889                     return Workspace;
43890                 })();
43891                 controllers.Workspace = Workspace;
43892             })(controllers = app.controllers || (app.controllers = {}));
43893         })(app || (app = {}));
43894         var app;
43895         (function (app) {
43896             var controllers;
43897             (function (controllers) {
43898                 var History = (function () {
43899                     function History($scope) {
43900                         this.page = "History";
43901                     }
43902                     History.$inject = ['$scope'];
43903                     return History;
43904                 })();
43905                 controllers.History = History;
43906             })(controllers = app.controllers || (app.controllers = {}));
43907         })(app || (app = {}));
43908         var app;
43909         (function (app) {
43910             var controllers;
43911             (function (controllers) {
43912                 var SelectCommand = (function () {
43913                     function SelectCommand($scope, APIEndPoint) {
43914                         this.APIEndPoint = APIEndPoint;
43915                         var controller = this;
43916                         this.APIEndPoint
43917                             .getTags()
43918                             .$promise.then(function (result) {
43919                             controller.tags = result.info;
43920                         });
43921                         this.APIEndPoint
43922                             .getCommands()
43923                             .$promise.then(function (result) {
43924                             controller.commands = result.info;
43925                         });
43926                     }
43927                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
43928                     return SelectCommand;
43929                 })();
43930                 controllers.SelectCommand = SelectCommand;
43931             })(controllers = app.controllers || (app.controllers = {}));
43932         })(app || (app = {}));
43933         var app;
43934         (function (app) {
43935             'use strict';
43936             var appName = 'zephyr';
43937             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43938             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43939                 $urlRouterProvider.otherwise('/execution');
43940                 $locationProvider.html5Mode({
43941                     enabled: true,
43942                     requireBase: false
43943                 });
43944                 $stateProvider
43945                     .state('execution', {
43946                     url: '/execution',
43947                     templateUrl: 'templates/execution.html',
43948                     controller: 'executionController',
43949                     controllerAs: 'c'
43950                 })
43951                     .state('workspace', {
43952                     url: '/workspace',
43953                     templateUrl: 'templates/workspace.html',
43954                     controller: 'workspaceController',
43955                     controllerAs: 'c'
43956                 })
43957                     .state('history', {
43958                     url: '/history',
43959                     templateUrl: 'templates/history.html',
43960                     controller: 'historyController',
43961                     controllerAs: 'c'
43962                 });
43963             });
43964             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43965             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
43966             app.zephyr.service('MyModal', app.services.MyModal);
43967             app.zephyr.controller('executionController', app.controllers.Execution);
43968             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43969             app.zephyr.controller('historyController', app.controllers.History);
43970             app.zephyr.controller('commandController', app.directives.CommandController);
43971             app.zephyr.controller('optionController', app.directives.OptionController);
43972             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43973             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43974             app.zephyr.directive('command', app.directives.Command.Factory());
43975             app.zephyr.directive('option', app.directives.Option.Factory());
43976             app.zephyr.directive('directory', app.directives.Directory.Factory());
43977         })(app || (app = {}));
43978
43979
43980 /***/ },
43981 /* 14 */
43982 /***/ function(module, exports) {
43983
43984         var app;
43985         (function (app) {
43986             var declares;
43987             (function (declares) {
43988                 var CommandInfo = (function () {
43989                     function CommandInfo(name) {
43990                         this.name = name;
43991                     }
43992                     return CommandInfo;
43993                 })();
43994                 declares.CommandInfo = CommandInfo;
43995             })(declares = app.declares || (app.declares = {}));
43996         })(app || (app = {}));
43997         var app;
43998         (function (app) {
43999             var services;
44000             (function (services) {
44001                 var APIEndPoint = (function () {
44002                     function APIEndPoint($resource, $http) {
44003                         this.$resource = $resource;
44004                         this.$http = $http;
44005                     }
44006                     APIEndPoint.prototype.resource = function (endPoint, data) {
44007                         var customAction = {
44008                             method: 'GET',
44009                             isArray: false
44010                         };
44011                         var execute = {
44012                             method: 'POST',
44013                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
44014                         };
44015                         return this.$resource(endPoint, {}, { execute: execute });
44016                     };
44017                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44018                         var endPoint = '/api/v1/optionControlFile/' + command;
44019                         return this.resource(endPoint, {}).get();
44020                     };
44021                     APIEndPoint.prototype.getFiles = function (fileId) {
44022                         var endPoint = '/api/v1/workspace';
44023                         if (fileId) {
44024                             endPoint += '/' + fileId;
44025                         }
44026                         return this.resource(endPoint, {}).get();
44027                     };
44028                     APIEndPoint.prototype.getDirectories = function () {
44029                         var endPoint = '/api/v1/all/workspace/directory';
44030                         return this.resource(endPoint, {}).get();
44031                     };
44032                     APIEndPoint.prototype.getTags = function () {
44033                         var endPoint = '/api/v1/tagList';
44034                         return this.resource(endPoint, {}).get();
44035                     };
44036                     APIEndPoint.prototype.getCommands = function () {
44037                         var endPoint = '/api/v1/commandList';
44038                         return this.resource(endPoint, {}).get();
44039                     };
44040                     APIEndPoint.prototype.execute = function (data) {
44041                         var endPoint = '/api/v1/execution';
44042                         var fd = new FormData();
44043                         fd.append('data', data);
44044                         return this.$http.post(endPoint, fd, {
44045                             headers: { 'Content-Type': undefined },
44046                             transformRequest: angular.identity
44047                         });
44048                     };
44049                     return APIEndPoint;
44050                 })();
44051                 services.APIEndPoint = APIEndPoint;
44052             })(services = app.services || (app.services = {}));
44053         })(app || (app = {}));
44054         var app;
44055         (function (app) {
44056             var services;
44057             (function (services) {
44058                 var MyModal = (function () {
44059                     function MyModal($uibModal) {
44060                         this.$uibModal = $uibModal;
44061                         this.modalOption = {
44062                             backdrop: true,
44063                             controller: null,
44064                             templateUrl: null,
44065                             size: null
44066                         };
44067                     }
44068                     MyModal.prototype.open = function (modalName) {
44069                         if (modalName === 'SelectCommand') {
44070                             this.modalOption.templateUrl = 'templates/select-command.html';
44071                             this.modalOption.size = 'lg';
44072                         }
44073                         return this.$uibModal.open(this.modalOption);
44074                     };
44075                     MyModal.prototype.selectCommand = function () {
44076                         this.modalOption.templateUrl = 'templates/select-command.html';
44077                         this.modalOption.controller = 'selectCommandController';
44078                         this.modalOption.controllerAs = 'c';
44079                         this.modalOption.size = 'lg';
44080                         return this.$uibModal.open(this.modalOption);
44081                     };
44082                     return MyModal;
44083                 })();
44084                 services.MyModal = MyModal;
44085             })(services = app.services || (app.services = {}));
44086         })(app || (app = {}));
44087         var app;
44088         (function (app) {
44089             var directives;
44090             (function (directives) {
44091                 var Command = (function () {
44092                     function Command() {
44093                         this.restrict = 'E';
44094                         this.replace = true;
44095                         this.scope = true;
44096                         this.controller = 'commandController';
44097                         this.controllerAs = 'ctrl';
44098                         this.bindToController = {
44099                             index: '=',
44100                             name: '=',
44101                             remove: '&',
44102                             list: '='
44103                         };
44104                         this.templateUrl = 'templates/command.html';
44105                     }
44106                     Command.Factory = function () {
44107                         var directive = function () {
44108                             return new Command();
44109                         };
44110                         directive.$inject = [];
44111                         return directive;
44112                     };
44113                     return Command;
44114                 })();
44115                 directives.Command = Command;
44116                 var CommandController = (function () {
44117                     function CommandController(APIEndPoint, $scope) {
44118                         this.APIEndPoint = APIEndPoint;
44119                         this.$scope = $scope;
44120                         var controller = this;
44121                         this.APIEndPoint
44122                             .getOptionControlFile('mrcImageNoiseAdd')
44123                             .$promise
44124                             .then(function (result) {
44125                             controller.options = result.info;
44126                         });
44127                         this.APIEndPoint
44128                             .getDirectories()
44129                             .$promise
44130                             .then(function (result) {
44131                             controller.dirs = result.info;
44132                         });
44133                         this.heading = "[" + this.index + "]: dcdFilePring";
44134                         this.isOpen = true;
44135                         this.$scope.$on('close', function () {
44136                             controller.isOpen = false;
44137                         });
44138                     }
44139                     CommandController.prototype.submit = function () {
44140                         var opt = [];
44141                         angular.forEach(this.options, function (option) {
44142                             var obj = {
44143                                 name: option.option,
44144                                 arguments: []
44145                             };
44146                             angular.forEach(option.arg, function (arg) {
44147                                 if (arg.input) {
44148                                     if (typeof arg.input === 'object') {
44149                                         obj.arguments.push(arg.input.name);
44150                                     }
44151                                     else {
44152                                         obj.arguments.push(arg.input);
44153                                     }
44154                                 }
44155                             });
44156                             if (obj.arguments.length > 0) {
44157                                 opt.push(obj);
44158                             }
44159                         });
44160                         var execObj = {
44161                             command: this.name,
44162                             workspace: this.workspace.fileId,
44163                             options: opt
44164                         };
44165                         this.APIEndPoint
44166                             .execute(JSON.stringify(execObj))
44167                             .then(function (result) {
44168                             console.log(result);
44169                         });
44170                     };
44171                     CommandController.prototype.removeMySelf = function (index) {
44172                         this.remove()(index, this.list);
44173                     };
44174                     CommandController.prototype.reloadFiles = function () {
44175                         var _this = this;
44176                         var fileId = this.workspace.fileId;
44177                         this.APIEndPoint
44178                             .getFiles(fileId)
44179                             .$promise
44180                             .then(function (result) {
44181                             var status = result.status;
44182                             if (status === 'success') {
44183                                 _this.files = result.info;
44184                             }
44185                             else {
44186                                 console.log(result.message);
44187                             }
44188                         });
44189                     };
44190                     CommandController.prototype.debug = function () {
44191                         console.log(this.files);
44192                         console.log(this.files);
44193                         console.log(this.workspace);
44194                     };
44195                     CommandController.$inject = ['APIEndPoint', '$scope'];
44196                     return CommandController;
44197                 })();
44198                 directives.CommandController = CommandController;
44199             })(directives = app.directives || (app.directives = {}));
44200         })(app || (app = {}));
44201         var app;
44202         (function (app) {
44203             var directives;
44204             (function (directives) {
44205                 var HeaderMenu = (function () {
44206                     function HeaderMenu() {
44207                         this.restrict = 'E';
44208                         this.replace = true;
44209                         this.templateUrl = 'templates/header-menu.html';
44210                     }
44211                     HeaderMenu.Factory = function () {
44212                         var directive = function () {
44213                             return new HeaderMenu();
44214                         };
44215                         return directive;
44216                     };
44217                     return HeaderMenu;
44218                 })();
44219                 directives.HeaderMenu = HeaderMenu;
44220             })(directives = app.directives || (app.directives = {}));
44221         })(app || (app = {}));
44222         var app;
44223         (function (app) {
44224             var directives;
44225             (function (directives) {
44226                 var Option = (function () {
44227                     function Option() {
44228                         this.restrict = 'E';
44229                         this.replace = true;
44230                         this.controller = 'optionController';
44231                         this.bindToController = {
44232                             info: '=',
44233                             files: '='
44234                         };
44235                         this.scope = true;
44236                         this.templateUrl = 'templates/option.html';
44237                         this.controllerAs = 'ctrl';
44238                     }
44239                     Option.Factory = function () {
44240                         var directive = function () {
44241                             return new Option();
44242                         };
44243                         directive.$inject = [];
44244                         return directive;
44245                     };
44246                     return Option;
44247                 })();
44248                 directives.Option = Option;
44249                 var OptionController = (function () {
44250                     function OptionController() {
44251                         var controller = this;
44252                         angular.forEach(controller.info.arg, function (arg) {
44253                             if (arg.initialValue) {
44254                                 if (arg.formType === 'number') {
44255                                     arg.input = parseInt(arg.initialValue);
44256                                 }
44257                                 else {
44258                                     arg.input = arg.initialValue;
44259                                 }
44260                             }
44261                         });
44262                     }
44263                     OptionController.$inject = [];
44264                     return OptionController;
44265                 })();
44266                 directives.OptionController = OptionController;
44267             })(directives = app.directives || (app.directives = {}));
44268         })(app || (app = {}));
44269         var app;
44270         (function (app) {
44271             var directives;
44272             (function (directives) {
44273                 var Directory = (function () {
44274                     function Directory() {
44275                         this.restrict = 'E';
44276                         this.replace = true;
44277                         this.controller = 'directoryController';
44278                         this.controllerAs = 'ctrl';
44279                         this.bindToController = {
44280                             info: '=',
44281                             add: '&',
44282                             list: '=',
44283                             files: '='
44284                         };
44285                         this.templateUrl = 'templates/directory.html';
44286                     }
44287                     Directory.Factory = function () {
44288                         var directive = function () {
44289                             return new Directory();
44290                         };
44291                         return directive;
44292                     };
44293                     return Directory;
44294                 })();
44295                 directives.Directory = Directory;
44296                 var DirectoryController = (function () {
44297                     function DirectoryController(APIEndPoint, $scope) {
44298                         this.APIEndPoint = APIEndPoint;
44299                         this.$scope = $scope;
44300                         var controller = this;
44301                         this.APIEndPoint
44302                             .getFiles(this.info.fileId)
44303                             .$promise
44304                             .then(function (result) {
44305                             if (result.status === 'success') {
44306                                 controller.files = result.info;
44307                                 angular.forEach(result.info, function (file) {
44308                                     if (file.fileType === '0') {
44309                                         var o = file;
44310                                         if (controller.info.path === '/') {
44311                                             o.path = '/' + file.name;
44312                                         }
44313                                         else {
44314                                             o.path = controller.info.path + '/' + file.name;
44315                                         }
44316                                         controller.add()(o, controller.list);
44317                                     }
44318                                 });
44319                             }
44320                             ;
44321                         });
44322                     }
44323                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44324                     return DirectoryController;
44325                 })();
44326                 directives.DirectoryController = DirectoryController;
44327             })(directives = app.directives || (app.directives = {}));
44328         })(app || (app = {}));
44329         var app;
44330         (function (app) {
44331             var controllers;
44332             (function (controllers) {
44333                 var Execution = (function () {
44334                     function Execution(MyModal, $scope) {
44335                         this.MyModal = MyModal;
44336                         this.$scope = $scope;
44337                         this.commandInfoList = [];
44338                     }
44339                     ;
44340                     Execution.prototype.add = function () {
44341                         this.$scope.$broadcast('close');
44342                         this.MyModal.selectCommand();
44343                     };
44344                     Execution.prototype.open = function () {
44345                         var result = this.MyModal.open('SelectCommand');
44346                         console.log(result);
44347                     };
44348                     Execution.prototype.remove = function (index, list) {
44349                         list.splice(index, 1);
44350                     };
44351                     Execution.prototype.close = function () {
44352                         console.log("close");
44353                     };
44354                     Execution.$inject = ['MyModal', '$scope'];
44355                     return Execution;
44356                 })();
44357                 controllers.Execution = Execution;
44358             })(controllers = app.controllers || (app.controllers = {}));
44359         })(app || (app = {}));
44360         var app;
44361         (function (app) {
44362             var controllers;
44363             (function (controllers) {
44364                 var Workspace = (function () {
44365                     function Workspace($scope, APIEndPoint) {
44366                         this.$scope = $scope;
44367                         this.APIEndPoint = APIEndPoint;
44368                         this.directoryList = [];
44369                         var controller = this;
44370                         var directoryList = this.directoryList;
44371                         var o = {
44372                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44373                             name: '',
44374                             parentId: '',
44375                             fileType: '',
44376                             createdAt: '',
44377                             updatedAt: '',
44378                             path: '/'
44379                         };
44380                         directoryList.push(o);
44381                     }
44382                     Workspace.prototype.addDirectory = function (info, directoryList) {
44383                         directoryList.push(info);
44384                     };
44385                     Workspace.$inject = ['$scope', 'APIEndPoint'];
44386                     return Workspace;
44387                 })();
44388                 controllers.Workspace = Workspace;
44389             })(controllers = app.controllers || (app.controllers = {}));
44390         })(app || (app = {}));
44391         var app;
44392         (function (app) {
44393             var controllers;
44394             (function (controllers) {
44395                 var History = (function () {
44396                     function History($scope) {
44397                         this.page = "History";
44398                     }
44399                     History.$inject = ['$scope'];
44400                     return History;
44401                 })();
44402                 controllers.History = History;
44403             })(controllers = app.controllers || (app.controllers = {}));
44404         })(app || (app = {}));
44405         var app;
44406         (function (app) {
44407             var controllers;
44408             (function (controllers) {
44409                 var SelectCommand = (function () {
44410                     function SelectCommand($scope, APIEndPoint) {
44411                         this.APIEndPoint = APIEndPoint;
44412                         var controller = this;
44413                         this.APIEndPoint
44414                             .getTags()
44415                             .$promise.then(function (result) {
44416                             controller.tags = result.info;
44417                         });
44418                         this.APIEndPoint
44419                             .getCommands()
44420                             .$promise.then(function (result) {
44421                             controller.commands = result.info;
44422                         });
44423                     }
44424                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
44425                     return SelectCommand;
44426                 })();
44427                 controllers.SelectCommand = SelectCommand;
44428             })(controllers = app.controllers || (app.controllers = {}));
44429         })(app || (app = {}));
44430         var app;
44431         (function (app) {
44432             'use strict';
44433             var appName = 'zephyr';
44434             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44435             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44436                 $urlRouterProvider.otherwise('/execution');
44437                 $locationProvider.html5Mode({
44438                     enabled: true,
44439                     requireBase: false
44440                 });
44441                 $stateProvider
44442                     .state('execution', {
44443                     url: '/execution',
44444                     templateUrl: 'templates/execution.html',
44445                     controller: 'executionController',
44446                     controllerAs: 'c'
44447                 })
44448                     .state('workspace', {
44449                     url: '/workspace',
44450                     templateUrl: 'templates/workspace.html',
44451                     controller: 'workspaceController',
44452                     controllerAs: 'c'
44453                 })
44454                     .state('history', {
44455                     url: '/history',
44456                     templateUrl: 'templates/history.html',
44457                     controller: 'historyController',
44458                     controllerAs: 'c'
44459                 });
44460             });
44461             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44462             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
44463             app.zephyr.service('MyModal', app.services.MyModal);
44464             app.zephyr.controller('executionController', app.controllers.Execution);
44465             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44466             app.zephyr.controller('historyController', app.controllers.History);
44467             app.zephyr.controller('commandController', app.directives.CommandController);
44468             app.zephyr.controller('optionController', app.directives.OptionController);
44469             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44470             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44471             app.zephyr.directive('command', app.directives.Command.Factory());
44472             app.zephyr.directive('option', app.directives.Option.Factory());
44473             app.zephyr.directive('directory', app.directives.Directory.Factory());
44474         })(app || (app = {}));
44475
44476
44477 /***/ },
44478 /* 15 */
44479 /***/ function(module, exports) {
44480
44481         var app;
44482         (function (app) {
44483             var declares;
44484             (function (declares) {
44485                 var CommandInfo = (function () {
44486                     function CommandInfo(name) {
44487                         this.name = name;
44488                     }
44489                     return CommandInfo;
44490                 })();
44491                 declares.CommandInfo = CommandInfo;
44492             })(declares = app.declares || (app.declares = {}));
44493         })(app || (app = {}));
44494         var app;
44495         (function (app) {
44496             var services;
44497             (function (services) {
44498                 var APIEndPoint = (function () {
44499                     function APIEndPoint($resource, $http) {
44500                         this.$resource = $resource;
44501                         this.$http = $http;
44502                     }
44503                     APIEndPoint.prototype.resource = function (endPoint, data) {
44504                         var customAction = {
44505                             method: 'GET',
44506                             isArray: false
44507                         };
44508                         var execute = {
44509                             method: 'POST',
44510                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
44511                         };
44512                         return this.$resource(endPoint, {}, { execute: execute });
44513                     };
44514                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44515                         var endPoint = '/api/v1/optionControlFile/' + command;
44516                         return this.resource(endPoint, {}).get();
44517                     };
44518                     APIEndPoint.prototype.getFiles = function (fileId) {
44519                         var endPoint = '/api/v1/workspace';
44520                         if (fileId) {
44521                             endPoint += '/' + fileId;
44522                         }
44523                         return this.resource(endPoint, {}).get();
44524                     };
44525                     APIEndPoint.prototype.getDirectories = function () {
44526                         var endPoint = '/api/v1/all/workspace/directory';
44527                         return this.resource(endPoint, {}).get();
44528                     };
44529                     APIEndPoint.prototype.getTags = function () {
44530                         var endPoint = '/api/v1/tagList';
44531                         return this.resource(endPoint, {}).get();
44532                     };
44533                     APIEndPoint.prototype.getCommands = function () {
44534                         var endPoint = '/api/v1/commandList';
44535                         return this.resource(endPoint, {}).get();
44536                     };
44537                     APIEndPoint.prototype.execute = function (data) {
44538                         var endPoint = '/api/v1/execution';
44539                         var fd = new FormData();
44540                         fd.append('data', data);
44541                         return this.$http.post(endPoint, fd, {
44542                             headers: { 'Content-Type': undefined },
44543                             transformRequest: angular.identity
44544                         });
44545                     };
44546                     return APIEndPoint;
44547                 })();
44548                 services.APIEndPoint = APIEndPoint;
44549             })(services = app.services || (app.services = {}));
44550         })(app || (app = {}));
44551         var app;
44552         (function (app) {
44553             var services;
44554             (function (services) {
44555                 var MyModal = (function () {
44556                     function MyModal($uibModal) {
44557                         this.$uibModal = $uibModal;
44558                         this.modalOption = {
44559                             backdrop: true,
44560                             controller: null,
44561                             templateUrl: null,
44562                             size: null
44563                         };
44564                     }
44565                     MyModal.prototype.open = function (modalName) {
44566                         if (modalName === 'SelectCommand') {
44567                             this.modalOption.templateUrl = 'templates/select-command.html';
44568                             this.modalOption.size = 'lg';
44569                         }
44570                         return this.$uibModal.open(this.modalOption);
44571                     };
44572                     MyModal.prototype.selectCommand = function () {
44573                         this.modalOption.templateUrl = 'templates/select-command.html';
44574                         this.modalOption.controller = 'selectCommandController';
44575                         this.modalOption.controllerAs = 'c';
44576                         this.modalOption.size = 'lg';
44577                         return this.$uibModal.open(this.modalOption);
44578                     };
44579                     return MyModal;
44580                 })();
44581                 services.MyModal = MyModal;
44582             })(services = app.services || (app.services = {}));
44583         })(app || (app = {}));
44584         var app;
44585         (function (app) {
44586             var directives;
44587             (function (directives) {
44588                 var Command = (function () {
44589                     function Command() {
44590                         this.restrict = 'E';
44591                         this.replace = true;
44592                         this.scope = true;
44593                         this.controller = 'commandController';
44594                         this.controllerAs = 'ctrl';
44595                         this.bindToController = {
44596                             index: '=',
44597                             name: '=',
44598                             remove: '&',
44599                             list: '='
44600                         };
44601                         this.templateUrl = 'templates/command.html';
44602                     }
44603                     Command.Factory = function () {
44604                         var directive = function () {
44605                             return new Command();
44606                         };
44607                         directive.$inject = [];
44608                         return directive;
44609                     };
44610                     return Command;
44611                 })();
44612                 directives.Command = Command;
44613                 var CommandController = (function () {
44614                     function CommandController(APIEndPoint, $scope) {
44615                         this.APIEndPoint = APIEndPoint;
44616                         this.$scope = $scope;
44617                         var controller = this;
44618                         this.APIEndPoint
44619                             .getOptionControlFile('mrcImageNoiseAdd')
44620                             .$promise
44621                             .then(function (result) {
44622                             controller.options = result.info;
44623                         });
44624                         this.APIEndPoint
44625                             .getDirectories()
44626                             .$promise
44627                             .then(function (result) {
44628                             controller.dirs = result.info;
44629                         });
44630                         this.heading = "[" + this.index + "]: dcdFilePring";
44631                         this.isOpen = true;
44632                         this.$scope.$on('close', function () {
44633                             controller.isOpen = false;
44634                         });
44635                     }
44636                     CommandController.prototype.submit = function () {
44637                         var opt = [];
44638                         angular.forEach(this.options, function (option) {
44639                             var obj = {
44640                                 name: option.option,
44641                                 arguments: []
44642                             };
44643                             angular.forEach(option.arg, function (arg) {
44644                                 if (arg.input) {
44645                                     if (typeof arg.input === 'object') {
44646                                         obj.arguments.push(arg.input.name);
44647                                     }
44648                                     else {
44649                                         obj.arguments.push(arg.input);
44650                                     }
44651                                 }
44652                             });
44653                             if (obj.arguments.length > 0) {
44654                                 opt.push(obj);
44655                             }
44656                         });
44657                         var execObj = {
44658                             command: this.name,
44659                             workspace: this.workspace.fileId,
44660                             options: opt
44661                         };
44662                         this.APIEndPoint
44663                             .execute(JSON.stringify(execObj))
44664                             .then(function (result) {
44665                             console.log(result);
44666                         });
44667                     };
44668                     CommandController.prototype.removeMySelf = function (index) {
44669                         this.remove()(index, this.list);
44670                     };
44671                     CommandController.prototype.reloadFiles = function () {
44672                         var _this = this;
44673                         var fileId = this.workspace.fileId;
44674                         this.APIEndPoint
44675                             .getFiles(fileId)
44676                             .$promise
44677                             .then(function (result) {
44678                             var status = result.status;
44679                             if (status === 'success') {
44680                                 _this.files = result.info;
44681                             }
44682                             else {
44683                                 console.log(result.message);
44684                             }
44685                         });
44686                     };
44687                     CommandController.prototype.debug = function () {
44688                         console.log(this.files);
44689                         console.log(this.files);
44690                         console.log(this.workspace);
44691                     };
44692                     CommandController.$inject = ['APIEndPoint', '$scope'];
44693                     return CommandController;
44694                 })();
44695                 directives.CommandController = CommandController;
44696             })(directives = app.directives || (app.directives = {}));
44697         })(app || (app = {}));
44698         var app;
44699         (function (app) {
44700             var directives;
44701             (function (directives) {
44702                 var HeaderMenu = (function () {
44703                     function HeaderMenu() {
44704                         this.restrict = 'E';
44705                         this.replace = true;
44706                         this.templateUrl = 'templates/header-menu.html';
44707                     }
44708                     HeaderMenu.Factory = function () {
44709                         var directive = function () {
44710                             return new HeaderMenu();
44711                         };
44712                         return directive;
44713                     };
44714                     return HeaderMenu;
44715                 })();
44716                 directives.HeaderMenu = HeaderMenu;
44717             })(directives = app.directives || (app.directives = {}));
44718         })(app || (app = {}));
44719         var app;
44720         (function (app) {
44721             var directives;
44722             (function (directives) {
44723                 var Option = (function () {
44724                     function Option() {
44725                         this.restrict = 'E';
44726                         this.replace = true;
44727                         this.controller = 'optionController';
44728                         this.bindToController = {
44729                             info: '=',
44730                             files: '='
44731                         };
44732                         this.scope = true;
44733                         this.templateUrl = 'templates/option.html';
44734                         this.controllerAs = 'ctrl';
44735                     }
44736                     Option.Factory = function () {
44737                         var directive = function () {
44738                             return new Option();
44739                         };
44740                         directive.$inject = [];
44741                         return directive;
44742                     };
44743                     return Option;
44744                 })();
44745                 directives.Option = Option;
44746                 var OptionController = (function () {
44747                     function OptionController() {
44748                         var controller = this;
44749                         angular.forEach(controller.info.arg, function (arg) {
44750                             if (arg.initialValue) {
44751                                 if (arg.formType === 'number') {
44752                                     arg.input = parseInt(arg.initialValue);
44753                                 }
44754                                 else {
44755                                     arg.input = arg.initialValue;
44756                                 }
44757                             }
44758                         });
44759                     }
44760                     OptionController.$inject = [];
44761                     return OptionController;
44762                 })();
44763                 directives.OptionController = OptionController;
44764             })(directives = app.directives || (app.directives = {}));
44765         })(app || (app = {}));
44766         var app;
44767         (function (app) {
44768             var directives;
44769             (function (directives) {
44770                 var Directory = (function () {
44771                     function Directory() {
44772                         this.restrict = 'E';
44773                         this.replace = true;
44774                         this.controller = 'directoryController';
44775                         this.controllerAs = 'ctrl';
44776                         this.bindToController = {
44777                             info: '=',
44778                             add: '&',
44779                             list: '=',
44780                             files: '='
44781                         };
44782                         this.templateUrl = 'templates/directory.html';
44783                     }
44784                     Directory.Factory = function () {
44785                         var directive = function () {
44786                             return new Directory();
44787                         };
44788                         return directive;
44789                     };
44790                     return Directory;
44791                 })();
44792                 directives.Directory = Directory;
44793                 var DirectoryController = (function () {
44794                     function DirectoryController(APIEndPoint, $scope) {
44795                         this.APIEndPoint = APIEndPoint;
44796                         this.$scope = $scope;
44797                         var controller = this;
44798                         this.APIEndPoint
44799                             .getFiles(this.info.fileId)
44800                             .$promise
44801                             .then(function (result) {
44802                             if (result.status === 'success') {
44803                                 controller.files = result.info;
44804                                 angular.forEach(result.info, function (file) {
44805                                     if (file.fileType === '0') {
44806                                         var o = file;
44807                                         if (controller.info.path === '/') {
44808                                             o.path = '/' + file.name;
44809                                         }
44810                                         else {
44811                                             o.path = controller.info.path + '/' + file.name;
44812                                         }
44813                                         controller.add()(o, controller.list);
44814                                     }
44815                                 });
44816                             }
44817                             ;
44818                         });
44819                     }
44820                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44821                     return DirectoryController;
44822                 })();
44823                 directives.DirectoryController = DirectoryController;
44824             })(directives = app.directives || (app.directives = {}));
44825         })(app || (app = {}));
44826         var app;
44827         (function (app) {
44828             var controllers;
44829             (function (controllers) {
44830                 var Execution = (function () {
44831                     function Execution(MyModal, $scope) {
44832                         this.MyModal = MyModal;
44833                         this.$scope = $scope;
44834                         this.commandInfoList = [];
44835                     }
44836                     ;
44837                     Execution.prototype.add = function () {
44838                         this.$scope.$broadcast('close');
44839                         this.MyModal.selectCommand();
44840                     };
44841                     Execution.prototype.open = function () {
44842                         var result = this.MyModal.open('SelectCommand');
44843                         console.log(result);
44844                     };
44845                     Execution.prototype.remove = function (index, list) {
44846                         list.splice(index, 1);
44847                     };
44848                     Execution.prototype.close = function () {
44849                         console.log("close");
44850                     };
44851                     Execution.$inject = ['MyModal', '$scope'];
44852                     return Execution;
44853                 })();
44854                 controllers.Execution = Execution;
44855             })(controllers = app.controllers || (app.controllers = {}));
44856         })(app || (app = {}));
44857         var app;
44858         (function (app) {
44859             var controllers;
44860             (function (controllers) {
44861                 var Workspace = (function () {
44862                     function Workspace($scope, APIEndPoint) {
44863                         this.$scope = $scope;
44864                         this.APIEndPoint = APIEndPoint;
44865                         this.directoryList = [];
44866                         var controller = this;
44867                         var directoryList = this.directoryList;
44868                         var o = {
44869                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44870                             name: '',
44871                             parentId: '',
44872                             fileType: '',
44873                             createdAt: '',
44874                             updatedAt: '',
44875                             path: '/'
44876                         };
44877                         directoryList.push(o);
44878                     }
44879                     Workspace.prototype.addDirectory = function (info, directoryList) {
44880                         directoryList.push(info);
44881                     };
44882                     Workspace.$inject = ['$scope', 'APIEndPoint'];
44883                     return Workspace;
44884                 })();
44885                 controllers.Workspace = Workspace;
44886             })(controllers = app.controllers || (app.controllers = {}));
44887         })(app || (app = {}));
44888         var app;
44889         (function (app) {
44890             var controllers;
44891             (function (controllers) {
44892                 var History = (function () {
44893                     function History($scope) {
44894                         this.page = "History";
44895                     }
44896                     History.$inject = ['$scope'];
44897                     return History;
44898                 })();
44899                 controllers.History = History;
44900             })(controllers = app.controllers || (app.controllers = {}));
44901         })(app || (app = {}));
44902         var app;
44903         (function (app) {
44904             var controllers;
44905             (function (controllers) {
44906                 var SelectCommand = (function () {
44907                     function SelectCommand($scope, APIEndPoint) {
44908                         this.APIEndPoint = APIEndPoint;
44909                         var controller = this;
44910                         this.APIEndPoint
44911                             .getTags()
44912                             .$promise.then(function (result) {
44913                             controller.tags = result.info;
44914                         });
44915                         this.APIEndPoint
44916                             .getCommands()
44917                             .$promise.then(function (result) {
44918                             controller.commands = result.info;
44919                         });
44920                     }
44921                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
44922                     return SelectCommand;
44923                 })();
44924                 controllers.SelectCommand = SelectCommand;
44925             })(controllers = app.controllers || (app.controllers = {}));
44926         })(app || (app = {}));
44927         var app;
44928         (function (app) {
44929             'use strict';
44930             var appName = 'zephyr';
44931             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44932             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44933                 $urlRouterProvider.otherwise('/execution');
44934                 $locationProvider.html5Mode({
44935                     enabled: true,
44936                     requireBase: false
44937                 });
44938                 $stateProvider
44939                     .state('execution', {
44940                     url: '/execution',
44941                     templateUrl: 'templates/execution.html',
44942                     controller: 'executionController',
44943                     controllerAs: 'c'
44944                 })
44945                     .state('workspace', {
44946                     url: '/workspace',
44947                     templateUrl: 'templates/workspace.html',
44948                     controller: 'workspaceController',
44949                     controllerAs: 'c'
44950                 })
44951                     .state('history', {
44952                     url: '/history',
44953                     templateUrl: 'templates/history.html',
44954                     controller: 'historyController',
44955                     controllerAs: 'c'
44956                 });
44957             });
44958             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44959             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
44960             app.zephyr.service('MyModal', app.services.MyModal);
44961             app.zephyr.controller('executionController', app.controllers.Execution);
44962             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44963             app.zephyr.controller('historyController', app.controllers.History);
44964             app.zephyr.controller('commandController', app.directives.CommandController);
44965             app.zephyr.controller('optionController', app.directives.OptionController);
44966             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44967             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44968             app.zephyr.directive('command', app.directives.Command.Factory());
44969             app.zephyr.directive('option', app.directives.Option.Factory());
44970             app.zephyr.directive('directory', app.directives.Directory.Factory());
44971         })(app || (app = {}));
44972
44973
44974 /***/ },
44975 /* 16 */
44976 /***/ function(module, exports) {
44977
44978         var app;
44979         (function (app) {
44980             var declares;
44981             (function (declares) {
44982                 var CommandInfo = (function () {
44983                     function CommandInfo(name) {
44984                         this.name = name;
44985                     }
44986                     return CommandInfo;
44987                 })();
44988                 declares.CommandInfo = CommandInfo;
44989             })(declares = app.declares || (app.declares = {}));
44990         })(app || (app = {}));
44991         var app;
44992         (function (app) {
44993             var services;
44994             (function (services) {
44995                 var APIEndPoint = (function () {
44996                     function APIEndPoint($resource, $http) {
44997                         this.$resource = $resource;
44998                         this.$http = $http;
44999                     }
45000                     APIEndPoint.prototype.resource = function (endPoint, data) {
45001                         var customAction = {
45002                             method: 'GET',
45003                             isArray: false
45004                         };
45005                         var execute = {
45006                             method: 'POST',
45007                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
45008                         };
45009                         return this.$resource(endPoint, {}, { execute: execute });
45010                     };
45011                     APIEndPoint.prototype.getOptionControlFile = function (command) {
45012                         var endPoint = '/api/v1/optionControlFile/' + command;
45013                         return this.resource(endPoint, {}).get();
45014                     };
45015                     APIEndPoint.prototype.getFiles = function (fileId) {
45016                         var endPoint = '/api/v1/workspace';
45017                         if (fileId) {
45018                             endPoint += '/' + fileId;
45019                         }
45020                         return this.resource(endPoint, {}).get();
45021                     };
45022                     APIEndPoint.prototype.getDirectories = function () {
45023                         var endPoint = '/api/v1/all/workspace/directory';
45024                         return this.resource(endPoint, {}).get();
45025                     };
45026                     APIEndPoint.prototype.getTags = function () {
45027                         var endPoint = '/api/v1/tagList';
45028                         return this.resource(endPoint, {}).get();
45029                     };
45030                     APIEndPoint.prototype.getCommands = function () {
45031                         var endPoint = '/api/v1/commandList';
45032                         return this.resource(endPoint, {}).get();
45033                     };
45034                     APIEndPoint.prototype.execute = function (data) {
45035                         var endPoint = '/api/v1/execution';
45036                         var fd = new FormData();
45037                         fd.append('data', data);
45038                         return this.$http.post(endPoint, fd, {
45039                             headers: { 'Content-Type': undefined },
45040                             transformRequest: angular.identity
45041                         });
45042                     };
45043                     return APIEndPoint;
45044                 })();
45045                 services.APIEndPoint = APIEndPoint;
45046             })(services = app.services || (app.services = {}));
45047         })(app || (app = {}));
45048         var app;
45049         (function (app) {
45050             var services;
45051             (function (services) {
45052                 var MyModal = (function () {
45053                     function MyModal($uibModal) {
45054                         this.$uibModal = $uibModal;
45055                         this.modalOption = {
45056                             backdrop: true,
45057                             controller: null,
45058                             templateUrl: null,
45059                             size: null
45060                         };
45061                     }
45062                     MyModal.prototype.open = function (modalName) {
45063                         if (modalName === 'SelectCommand') {
45064                             this.modalOption.templateUrl = 'templates/select-command.html';
45065                             this.modalOption.size = 'lg';
45066                         }
45067                         return this.$uibModal.open(this.modalOption);
45068                     };
45069                     MyModal.prototype.selectCommand = function () {
45070                         this.modalOption.templateUrl = 'templates/select-command.html';
45071                         this.modalOption.controller = 'selectCommandController';
45072                         this.modalOption.controllerAs = 'c';
45073                         this.modalOption.size = 'lg';
45074                         return this.$uibModal.open(this.modalOption);
45075                     };
45076                     return MyModal;
45077                 })();
45078                 services.MyModal = MyModal;
45079             })(services = app.services || (app.services = {}));
45080         })(app || (app = {}));
45081         var app;
45082         (function (app) {
45083             var directives;
45084             (function (directives) {
45085                 var Command = (function () {
45086                     function Command() {
45087                         this.restrict = 'E';
45088                         this.replace = true;
45089                         this.scope = true;
45090                         this.controller = 'commandController';
45091                         this.controllerAs = 'ctrl';
45092                         this.bindToController = {
45093                             index: '=',
45094                             name: '=',
45095                             remove: '&',
45096                             list: '='
45097                         };
45098                         this.templateUrl = 'templates/command.html';
45099                     }
45100                     Command.Factory = function () {
45101                         var directive = function () {
45102                             return new Command();
45103                         };
45104                         directive.$inject = [];
45105                         return directive;
45106                     };
45107                     return Command;
45108                 })();
45109                 directives.Command = Command;
45110                 var CommandController = (function () {
45111                     function CommandController(APIEndPoint, $scope) {
45112                         this.APIEndPoint = APIEndPoint;
45113                         this.$scope = $scope;
45114                         var controller = this;
45115                         this.APIEndPoint
45116                             .getOptionControlFile('mrcImageNoiseAdd')
45117                             .$promise
45118                             .then(function (result) {
45119                             controller.options = result.info;
45120                         });
45121                         this.APIEndPoint
45122                             .getDirectories()
45123                             .$promise
45124                             .then(function (result) {
45125                             controller.dirs = result.info;
45126                         });
45127                         this.heading = "[" + this.index + "]: dcdFilePring";
45128                         this.isOpen = true;
45129                         this.$scope.$on('close', function () {
45130                             controller.isOpen = false;
45131                         });
45132                     }
45133                     CommandController.prototype.submit = function () {
45134                         var opt = [];
45135                         angular.forEach(this.options, function (option) {
45136                             var obj = {
45137                                 name: option.option,
45138                                 arguments: []
45139                             };
45140                             angular.forEach(option.arg, function (arg) {
45141                                 if (arg.input) {
45142                                     if (typeof arg.input === 'object') {
45143                                         obj.arguments.push(arg.input.name);
45144                                     }
45145                                     else {
45146                                         obj.arguments.push(arg.input);
45147                                     }
45148                                 }
45149                             });
45150                             if (obj.arguments.length > 0) {
45151                                 opt.push(obj);
45152                             }
45153                         });
45154                         var execObj = {
45155                             command: this.name,
45156                             workspace: this.workspace.fileId,
45157                             options: opt
45158                         };
45159                         this.APIEndPoint
45160                             .execute(JSON.stringify(execObj))
45161                             .then(function (result) {
45162                             console.log(result);
45163                         });
45164                     };
45165                     CommandController.prototype.removeMySelf = function (index) {
45166                         this.remove()(index, this.list);
45167                     };
45168                     CommandController.prototype.reloadFiles = function () {
45169                         var _this = this;
45170                         var fileId = this.workspace.fileId;
45171                         this.APIEndPoint
45172                             .getFiles(fileId)
45173                             .$promise
45174                             .then(function (result) {
45175                             var status = result.status;
45176                             if (status === 'success') {
45177                                 _this.files = result.info;
45178                             }
45179                             else {
45180                                 console.log(result.message);
45181                             }
45182                         });
45183                     };
45184                     CommandController.prototype.debug = function () {
45185                         console.log(this.files);
45186                         console.log(this.files);
45187                         console.log(this.workspace);
45188                     };
45189                     CommandController.$inject = ['APIEndPoint', '$scope'];
45190                     return CommandController;
45191                 })();
45192                 directives.CommandController = CommandController;
45193             })(directives = app.directives || (app.directives = {}));
45194         })(app || (app = {}));
45195         var app;
45196         (function (app) {
45197             var directives;
45198             (function (directives) {
45199                 var HeaderMenu = (function () {
45200                     function HeaderMenu() {
45201                         this.restrict = 'E';
45202                         this.replace = true;
45203                         this.templateUrl = 'templates/header-menu.html';
45204                     }
45205                     HeaderMenu.Factory = function () {
45206                         var directive = function () {
45207                             return new HeaderMenu();
45208                         };
45209                         return directive;
45210                     };
45211                     return HeaderMenu;
45212                 })();
45213                 directives.HeaderMenu = HeaderMenu;
45214             })(directives = app.directives || (app.directives = {}));
45215         })(app || (app = {}));
45216         var app;
45217         (function (app) {
45218             var directives;
45219             (function (directives) {
45220                 var Option = (function () {
45221                     function Option() {
45222                         this.restrict = 'E';
45223                         this.replace = true;
45224                         this.controller = 'optionController';
45225                         this.bindToController = {
45226                             info: '=',
45227                             files: '='
45228                         };
45229                         this.scope = true;
45230                         this.templateUrl = 'templates/option.html';
45231                         this.controllerAs = 'ctrl';
45232                     }
45233                     Option.Factory = function () {
45234                         var directive = function () {
45235                             return new Option();
45236                         };
45237                         directive.$inject = [];
45238                         return directive;
45239                     };
45240                     return Option;
45241                 })();
45242                 directives.Option = Option;
45243                 var OptionController = (function () {
45244                     function OptionController() {
45245                         var controller = this;
45246                         angular.forEach(controller.info.arg, function (arg) {
45247                             if (arg.initialValue) {
45248                                 if (arg.formType === 'number') {
45249                                     arg.input = parseInt(arg.initialValue);
45250                                 }
45251                                 else {
45252                                     arg.input = arg.initialValue;
45253                                 }
45254                             }
45255                         });
45256                     }
45257                     OptionController.$inject = [];
45258                     return OptionController;
45259                 })();
45260                 directives.OptionController = OptionController;
45261             })(directives = app.directives || (app.directives = {}));
45262         })(app || (app = {}));
45263         var app;
45264         (function (app) {
45265             var directives;
45266             (function (directives) {
45267                 var Directory = (function () {
45268                     function Directory() {
45269                         this.restrict = 'E';
45270                         this.replace = true;
45271                         this.controller = 'directoryController';
45272                         this.controllerAs = 'ctrl';
45273                         this.bindToController = {
45274                             info: '=',
45275                             add: '&',
45276                             list: '=',
45277                             files: '='
45278                         };
45279                         this.templateUrl = 'templates/directory.html';
45280                     }
45281                     Directory.Factory = function () {
45282                         var directive = function () {
45283                             return new Directory();
45284                         };
45285                         return directive;
45286                     };
45287                     return Directory;
45288                 })();
45289                 directives.Directory = Directory;
45290                 var DirectoryController = (function () {
45291                     function DirectoryController(APIEndPoint, $scope) {
45292                         this.APIEndPoint = APIEndPoint;
45293                         this.$scope = $scope;
45294                         var controller = this;
45295                         this.APIEndPoint
45296                             .getFiles(this.info.fileId)
45297                             .$promise
45298                             .then(function (result) {
45299                             if (result.status === 'success') {
45300                                 controller.files = result.info;
45301                                 angular.forEach(result.info, function (file) {
45302                                     if (file.fileType === '0') {
45303                                         var o = file;
45304                                         if (controller.info.path === '/') {
45305                                             o.path = '/' + file.name;
45306                                         }
45307                                         else {
45308                                             o.path = controller.info.path + '/' + file.name;
45309                                         }
45310                                         controller.add()(o, controller.list);
45311                                     }
45312                                 });
45313                             }
45314                             ;
45315                         });
45316                     }
45317                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45318                     return DirectoryController;
45319                 })();
45320                 directives.DirectoryController = DirectoryController;
45321             })(directives = app.directives || (app.directives = {}));
45322         })(app || (app = {}));
45323         var app;
45324         (function (app) {
45325             var controllers;
45326             (function (controllers) {
45327                 var Execution = (function () {
45328                     function Execution(MyModal, $scope) {
45329                         this.MyModal = MyModal;
45330                         this.$scope = $scope;
45331                         this.commandInfoList = [];
45332                     }
45333                     ;
45334                     Execution.prototype.add = function () {
45335                         this.$scope.$broadcast('close');
45336                         this.MyModal.selectCommand();
45337                     };
45338                     Execution.prototype.open = function () {
45339                         var result = this.MyModal.open('SelectCommand');
45340                         console.log(result);
45341                     };
45342                     Execution.prototype.remove = function (index, list) {
45343                         list.splice(index, 1);
45344                     };
45345                     Execution.prototype.close = function () {
45346                         console.log("close");
45347                     };
45348                     Execution.$inject = ['MyModal', '$scope'];
45349                     return Execution;
45350                 })();
45351                 controllers.Execution = Execution;
45352             })(controllers = app.controllers || (app.controllers = {}));
45353         })(app || (app = {}));
45354         var app;
45355         (function (app) {
45356             var controllers;
45357             (function (controllers) {
45358                 var Workspace = (function () {
45359                     function Workspace($scope, APIEndPoint) {
45360                         this.$scope = $scope;
45361                         this.APIEndPoint = APIEndPoint;
45362                         this.directoryList = [];
45363                         var controller = this;
45364                         var directoryList = this.directoryList;
45365                         var o = {
45366                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45367                             name: '',
45368                             parentId: '',
45369                             fileType: '',
45370                             createdAt: '',
45371                             updatedAt: '',
45372                             path: '/'
45373                         };
45374                         directoryList.push(o);
45375                     }
45376                     Workspace.prototype.addDirectory = function (info, directoryList) {
45377                         directoryList.push(info);
45378                     };
45379                     Workspace.$inject = ['$scope', 'APIEndPoint'];
45380                     return Workspace;
45381                 })();
45382                 controllers.Workspace = Workspace;
45383             })(controllers = app.controllers || (app.controllers = {}));
45384         })(app || (app = {}));
45385         var app;
45386         (function (app) {
45387             var controllers;
45388             (function (controllers) {
45389                 var History = (function () {
45390                     function History($scope) {
45391                         this.page = "History";
45392                     }
45393                     History.$inject = ['$scope'];
45394                     return History;
45395                 })();
45396                 controllers.History = History;
45397             })(controllers = app.controllers || (app.controllers = {}));
45398         })(app || (app = {}));
45399         var app;
45400         (function (app) {
45401             var controllers;
45402             (function (controllers) {
45403                 var SelectCommand = (function () {
45404                     function SelectCommand($scope, APIEndPoint) {
45405                         this.APIEndPoint = APIEndPoint;
45406                         var controller = this;
45407                         this.APIEndPoint
45408                             .getTags()
45409                             .$promise.then(function (result) {
45410                             controller.tags = result.info;
45411                         });
45412                         this.APIEndPoint
45413                             .getCommands()
45414                             .$promise.then(function (result) {
45415                             controller.commands = result.info;
45416                         });
45417                     }
45418                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
45419                     return SelectCommand;
45420                 })();
45421                 controllers.SelectCommand = SelectCommand;
45422             })(controllers = app.controllers || (app.controllers = {}));
45423         })(app || (app = {}));
45424         var app;
45425         (function (app) {
45426             'use strict';
45427             var appName = 'zephyr';
45428             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45429             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45430                 $urlRouterProvider.otherwise('/execution');
45431                 $locationProvider.html5Mode({
45432                     enabled: true,
45433                     requireBase: false
45434                 });
45435                 $stateProvider
45436                     .state('execution', {
45437                     url: '/execution',
45438                     templateUrl: 'templates/execution.html',
45439                     controller: 'executionController',
45440                     controllerAs: 'c'
45441                 })
45442                     .state('workspace', {
45443                     url: '/workspace',
45444                     templateUrl: 'templates/workspace.html',
45445                     controller: 'workspaceController',
45446                     controllerAs: 'c'
45447                 })
45448                     .state('history', {
45449                     url: '/history',
45450                     templateUrl: 'templates/history.html',
45451                     controller: 'historyController',
45452                     controllerAs: 'c'
45453                 });
45454             });
45455             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45456             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
45457             app.zephyr.service('MyModal', app.services.MyModal);
45458             app.zephyr.controller('executionController', app.controllers.Execution);
45459             app.zephyr.controller('workspaceController', app.controllers.Workspace);
45460             app.zephyr.controller('historyController', app.controllers.History);
45461             app.zephyr.controller('commandController', app.directives.CommandController);
45462             app.zephyr.controller('optionController', app.directives.OptionController);
45463             app.zephyr.controller('directoryController', app.directives.DirectoryController);
45464             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45465             app.zephyr.directive('command', app.directives.Command.Factory());
45466             app.zephyr.directive('option', app.directives.Option.Factory());
45467             app.zephyr.directive('directory', app.directives.Directory.Factory());
45468         })(app || (app = {}));
45469
45470
45471 /***/ },
45472 /* 17 */
45473 /***/ function(module, exports) {
45474
45475         var app;
45476         (function (app) {
45477             var declares;
45478             (function (declares) {
45479                 var CommandInfo = (function () {
45480                     function CommandInfo(name) {
45481                         this.name = name;
45482                     }
45483                     return CommandInfo;
45484                 })();
45485                 declares.CommandInfo = CommandInfo;
45486             })(declares = app.declares || (app.declares = {}));
45487         })(app || (app = {}));
45488         var app;
45489         (function (app) {
45490             var services;
45491             (function (services) {
45492                 var APIEndPoint = (function () {
45493                     function APIEndPoint($resource, $http) {
45494                         this.$resource = $resource;
45495                         this.$http = $http;
45496                     }
45497                     APIEndPoint.prototype.resource = function (endPoint, data) {
45498                         var customAction = {
45499                             method: 'GET',
45500                             isArray: false
45501                         };
45502                         var execute = {
45503                             method: 'POST',
45504                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
45505                         };
45506                         return this.$resource(endPoint, {}, { execute: execute });
45507                     };
45508                     APIEndPoint.prototype.getOptionControlFile = function (command) {
45509                         var endPoint = '/api/v1/optionControlFile/' + command;
45510                         return this.resource(endPoint, {}).get();
45511                     };
45512                     APIEndPoint.prototype.getFiles = function (fileId) {
45513                         var endPoint = '/api/v1/workspace';
45514                         if (fileId) {
45515                             endPoint += '/' + fileId;
45516                         }
45517                         return this.resource(endPoint, {}).get();
45518                     };
45519                     APIEndPoint.prototype.getDirectories = function () {
45520                         var endPoint = '/api/v1/all/workspace/directory';
45521                         return this.resource(endPoint, {}).get();
45522                     };
45523                     APIEndPoint.prototype.getTags = function () {
45524                         var endPoint = '/api/v1/tagList';
45525                         return this.resource(endPoint, {}).get();
45526                     };
45527                     APIEndPoint.prototype.getCommands = function () {
45528                         var endPoint = '/api/v1/commandList';
45529                         return this.resource(endPoint, {}).get();
45530                     };
45531                     APIEndPoint.prototype.execute = function (data) {
45532                         var endPoint = '/api/v1/execution';
45533                         var fd = new FormData();
45534                         fd.append('data', data);
45535                         return this.$http.post(endPoint, fd, {
45536                             headers: { 'Content-Type': undefined },
45537                             transformRequest: angular.identity
45538                         });
45539                     };
45540                     return APIEndPoint;
45541                 })();
45542                 services.APIEndPoint = APIEndPoint;
45543             })(services = app.services || (app.services = {}));
45544         })(app || (app = {}));
45545         var app;
45546         (function (app) {
45547             var services;
45548             (function (services) {
45549                 var MyModal = (function () {
45550                     function MyModal($uibModal) {
45551                         this.$uibModal = $uibModal;
45552                         this.modalOption = {
45553                             backdrop: true,
45554                             controller: null,
45555                             templateUrl: null,
45556                             size: null
45557                         };
45558                     }
45559                     MyModal.prototype.open = function (modalName) {
45560                         if (modalName === 'SelectCommand') {
45561                             this.modalOption.templateUrl = 'templates/select-command.html';
45562                             this.modalOption.size = 'lg';
45563                         }
45564                         return this.$uibModal.open(this.modalOption);
45565                     };
45566                     MyModal.prototype.selectCommand = function () {
45567                         this.modalOption.templateUrl = 'templates/select-command.html';
45568                         this.modalOption.controller = 'selectCommandController';
45569                         this.modalOption.controllerAs = 'c';
45570                         this.modalOption.size = 'lg';
45571                         return this.$uibModal.open(this.modalOption);
45572                     };
45573                     return MyModal;
45574                 })();
45575                 services.MyModal = MyModal;
45576             })(services = app.services || (app.services = {}));
45577         })(app || (app = {}));
45578         var app;
45579         (function (app) {
45580             var directives;
45581             (function (directives) {
45582                 var Command = (function () {
45583                     function Command() {
45584                         this.restrict = 'E';
45585                         this.replace = true;
45586                         this.scope = true;
45587                         this.controller = 'commandController';
45588                         this.controllerAs = 'ctrl';
45589                         this.bindToController = {
45590                             index: '=',
45591                             name: '=',
45592                             remove: '&',
45593                             list: '='
45594                         };
45595                         this.templateUrl = 'templates/command.html';
45596                     }
45597                     Command.Factory = function () {
45598                         var directive = function () {
45599                             return new Command();
45600                         };
45601                         directive.$inject = [];
45602                         return directive;
45603                     };
45604                     return Command;
45605                 })();
45606                 directives.Command = Command;
45607                 var CommandController = (function () {
45608                     function CommandController(APIEndPoint, $scope) {
45609                         this.APIEndPoint = APIEndPoint;
45610                         this.$scope = $scope;
45611                         var controller = this;
45612                         this.APIEndPoint
45613                             .getOptionControlFile('mrcImageNoiseAdd')
45614                             .$promise
45615                             .then(function (result) {
45616                             controller.options = result.info;
45617                         });
45618                         this.APIEndPoint
45619                             .getDirectories()
45620                             .$promise
45621                             .then(function (result) {
45622                             controller.dirs = result.info;
45623                         });
45624                         this.heading = "[" + this.index + "]: dcdFilePring";
45625                         this.isOpen = true;
45626                         this.$scope.$on('close', function () {
45627                             controller.isOpen = false;
45628                         });
45629                     }
45630                     CommandController.prototype.submit = function () {
45631                         var opt = [];
45632                         angular.forEach(this.options, function (option) {
45633                             var obj = {
45634                                 name: option.option,
45635                                 arguments: []
45636                             };
45637                             angular.forEach(option.arg, function (arg) {
45638                                 if (arg.input) {
45639                                     if (typeof arg.input === 'object') {
45640                                         obj.arguments.push(arg.input.name);
45641                                     }
45642                                     else {
45643                                         obj.arguments.push(arg.input);
45644                                     }
45645                                 }
45646                             });
45647                             if (obj.arguments.length > 0) {
45648                                 opt.push(obj);
45649                             }
45650                         });
45651                         var execObj = {
45652                             command: this.name,
45653                             workspace: this.workspace.fileId,
45654                             options: opt
45655                         };
45656                         this.APIEndPoint
45657                             .execute(JSON.stringify(execObj))
45658                             .then(function (result) {
45659                             console.log(result);
45660                         });
45661                     };
45662                     CommandController.prototype.removeMySelf = function (index) {
45663                         this.remove()(index, this.list);
45664                     };
45665                     CommandController.prototype.reloadFiles = function () {
45666                         var _this = this;
45667                         var fileId = this.workspace.fileId;
45668                         this.APIEndPoint
45669                             .getFiles(fileId)
45670                             .$promise
45671                             .then(function (result) {
45672                             var status = result.status;
45673                             if (status === 'success') {
45674                                 _this.files = result.info;
45675                             }
45676                             else {
45677                                 console.log(result.message);
45678                             }
45679                         });
45680                     };
45681                     CommandController.prototype.debug = function () {
45682                         console.log(this.files);
45683                         console.log(this.files);
45684                         console.log(this.workspace);
45685                     };
45686                     CommandController.$inject = ['APIEndPoint', '$scope'];
45687                     return CommandController;
45688                 })();
45689                 directives.CommandController = CommandController;
45690             })(directives = app.directives || (app.directives = {}));
45691         })(app || (app = {}));
45692         var app;
45693         (function (app) {
45694             var directives;
45695             (function (directives) {
45696                 var HeaderMenu = (function () {
45697                     function HeaderMenu() {
45698                         this.restrict = 'E';
45699                         this.replace = true;
45700                         this.templateUrl = 'templates/header-menu.html';
45701                     }
45702                     HeaderMenu.Factory = function () {
45703                         var directive = function () {
45704                             return new HeaderMenu();
45705                         };
45706                         return directive;
45707                     };
45708                     return HeaderMenu;
45709                 })();
45710                 directives.HeaderMenu = HeaderMenu;
45711             })(directives = app.directives || (app.directives = {}));
45712         })(app || (app = {}));
45713         var app;
45714         (function (app) {
45715             var directives;
45716             (function (directives) {
45717                 var Option = (function () {
45718                     function Option() {
45719                         this.restrict = 'E';
45720                         this.replace = true;
45721                         this.controller = 'optionController';
45722                         this.bindToController = {
45723                             info: '=',
45724                             files: '='
45725                         };
45726                         this.scope = true;
45727                         this.templateUrl = 'templates/option.html';
45728                         this.controllerAs = 'ctrl';
45729                     }
45730                     Option.Factory = function () {
45731                         var directive = function () {
45732                             return new Option();
45733                         };
45734                         directive.$inject = [];
45735                         return directive;
45736                     };
45737                     return Option;
45738                 })();
45739                 directives.Option = Option;
45740                 var OptionController = (function () {
45741                     function OptionController() {
45742                         var controller = this;
45743                         angular.forEach(controller.info.arg, function (arg) {
45744                             if (arg.initialValue) {
45745                                 if (arg.formType === 'number') {
45746                                     arg.input = parseInt(arg.initialValue);
45747                                 }
45748                                 else {
45749                                     arg.input = arg.initialValue;
45750                                 }
45751                             }
45752                         });
45753                     }
45754                     OptionController.$inject = [];
45755                     return OptionController;
45756                 })();
45757                 directives.OptionController = OptionController;
45758             })(directives = app.directives || (app.directives = {}));
45759         })(app || (app = {}));
45760         var app;
45761         (function (app) {
45762             var directives;
45763             (function (directives) {
45764                 var Directory = (function () {
45765                     function Directory() {
45766                         this.restrict = 'E';
45767                         this.replace = true;
45768                         this.controller = 'directoryController';
45769                         this.controllerAs = 'ctrl';
45770                         this.bindToController = {
45771                             info: '=',
45772                             add: '&',
45773                             list: '=',
45774                             files: '='
45775                         };
45776                         this.templateUrl = 'templates/directory.html';
45777                     }
45778                     Directory.Factory = function () {
45779                         var directive = function () {
45780                             return new Directory();
45781                         };
45782                         return directive;
45783                     };
45784                     return Directory;
45785                 })();
45786                 directives.Directory = Directory;
45787                 var DirectoryController = (function () {
45788                     function DirectoryController(APIEndPoint, $scope) {
45789                         this.APIEndPoint = APIEndPoint;
45790                         this.$scope = $scope;
45791                         var controller = this;
45792                         this.APIEndPoint
45793                             .getFiles(this.info.fileId)
45794                             .$promise
45795                             .then(function (result) {
45796                             if (result.status === 'success') {
45797                                 controller.files = result.info;
45798                                 angular.forEach(result.info, function (file) {
45799                                     if (file.fileType === '0') {
45800                                         var o = file;
45801                                         if (controller.info.path === '/') {
45802                                             o.path = '/' + file.name;
45803                                         }
45804                                         else {
45805                                             o.path = controller.info.path + '/' + file.name;
45806                                         }
45807                                         controller.add()(o, controller.list);
45808                                     }
45809                                 });
45810                             }
45811                             ;
45812                         });
45813                     }
45814                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45815                     return DirectoryController;
45816                 })();
45817                 directives.DirectoryController = DirectoryController;
45818             })(directives = app.directives || (app.directives = {}));
45819         })(app || (app = {}));
45820         var app;
45821         (function (app) {
45822             var controllers;
45823             (function (controllers) {
45824                 var Execution = (function () {
45825                     function Execution(MyModal, $scope) {
45826                         this.MyModal = MyModal;
45827                         this.$scope = $scope;
45828                         this.commandInfoList = [];
45829                     }
45830                     ;
45831                     Execution.prototype.add = function () {
45832                         this.$scope.$broadcast('close');
45833                         this.MyModal.selectCommand();
45834                     };
45835                     Execution.prototype.open = function () {
45836                         var result = this.MyModal.open('SelectCommand');
45837                         console.log(result);
45838                     };
45839                     Execution.prototype.remove = function (index, list) {
45840                         list.splice(index, 1);
45841                     };
45842                     Execution.prototype.close = function () {
45843                         console.log("close");
45844                     };
45845                     Execution.$inject = ['MyModal', '$scope'];
45846                     return Execution;
45847                 })();
45848                 controllers.Execution = Execution;
45849             })(controllers = app.controllers || (app.controllers = {}));
45850         })(app || (app = {}));
45851         var app;
45852         (function (app) {
45853             var controllers;
45854             (function (controllers) {
45855                 var Workspace = (function () {
45856                     function Workspace($scope, APIEndPoint) {
45857                         this.$scope = $scope;
45858                         this.APIEndPoint = APIEndPoint;
45859                         this.directoryList = [];
45860                         var controller = this;
45861                         var directoryList = this.directoryList;
45862                         var o = {
45863                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45864                             name: '',
45865                             parentId: '',
45866                             fileType: '',
45867                             createdAt: '',
45868                             updatedAt: '',
45869                             path: '/'
45870                         };
45871                         directoryList.push(o);
45872                     }
45873                     Workspace.prototype.addDirectory = function (info, directoryList) {
45874                         directoryList.push(info);
45875                     };
45876                     Workspace.$inject = ['$scope', 'APIEndPoint'];
45877                     return Workspace;
45878                 })();
45879                 controllers.Workspace = Workspace;
45880             })(controllers = app.controllers || (app.controllers = {}));
45881         })(app || (app = {}));
45882         var app;
45883         (function (app) {
45884             var controllers;
45885             (function (controllers) {
45886                 var History = (function () {
45887                     function History($scope) {
45888                         this.page = "History";
45889                     }
45890                     History.$inject = ['$scope'];
45891                     return History;
45892                 })();
45893                 controllers.History = History;
45894             })(controllers = app.controllers || (app.controllers = {}));
45895         })(app || (app = {}));
45896         var app;
45897         (function (app) {
45898             var controllers;
45899             (function (controllers) {
45900                 var SelectCommand = (function () {
45901                     function SelectCommand($scope, APIEndPoint) {
45902                         this.APIEndPoint = APIEndPoint;
45903                         var controller = this;
45904                         this.APIEndPoint
45905                             .getTags()
45906                             .$promise.then(function (result) {
45907                             controller.tags = result.info;
45908                         });
45909                         this.APIEndPoint
45910                             .getCommands()
45911                             .$promise.then(function (result) {
45912                             controller.commands = result.info;
45913                         });
45914                     }
45915                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
45916                     return SelectCommand;
45917                 })();
45918                 controllers.SelectCommand = SelectCommand;
45919             })(controllers = app.controllers || (app.controllers = {}));
45920         })(app || (app = {}));
45921         var app;
45922         (function (app) {
45923             'use strict';
45924             var appName = 'zephyr';
45925             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45926             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45927                 $urlRouterProvider.otherwise('/execution');
45928                 $locationProvider.html5Mode({
45929                     enabled: true,
45930                     requireBase: false
45931                 });
45932                 $stateProvider
45933                     .state('execution', {
45934                     url: '/execution',
45935                     templateUrl: 'templates/execution.html',
45936                     controller: 'executionController',
45937                     controllerAs: 'c'
45938                 })
45939                     .state('workspace', {
45940                     url: '/workspace',
45941                     templateUrl: 'templates/workspace.html',
45942                     controller: 'workspaceController',
45943                     controllerAs: 'c'
45944                 })
45945                     .state('history', {
45946                     url: '/history',
45947                     templateUrl: 'templates/history.html',
45948                     controller: 'historyController',
45949                     controllerAs: 'c'
45950                 });
45951             });
45952             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45953             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
45954             app.zephyr.service('MyModal', app.services.MyModal);
45955             app.zephyr.controller('executionController', app.controllers.Execution);
45956             app.zephyr.controller('workspaceController', app.controllers.Workspace);
45957             app.zephyr.controller('historyController', app.controllers.History);
45958             app.zephyr.controller('commandController', app.directives.CommandController);
45959             app.zephyr.controller('optionController', app.directives.OptionController);
45960             app.zephyr.controller('directoryController', app.directives.DirectoryController);
45961             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45962             app.zephyr.directive('command', app.directives.Command.Factory());
45963             app.zephyr.directive('option', app.directives.Option.Factory());
45964             app.zephyr.directive('directory', app.directives.Directory.Factory());
45965         })(app || (app = {}));
45966
45967
45968 /***/ },
45969 /* 18 */
45970 /***/ function(module, exports) {
45971
45972         var app;
45973         (function (app) {
45974             var declares;
45975             (function (declares) {
45976                 var CommandInfo = (function () {
45977                     function CommandInfo(name) {
45978                         this.name = name;
45979                     }
45980                     return CommandInfo;
45981                 })();
45982                 declares.CommandInfo = CommandInfo;
45983             })(declares = app.declares || (app.declares = {}));
45984         })(app || (app = {}));
45985         var app;
45986         (function (app) {
45987             var services;
45988             (function (services) {
45989                 var APIEndPoint = (function () {
45990                     function APIEndPoint($resource, $http) {
45991                         this.$resource = $resource;
45992                         this.$http = $http;
45993                     }
45994                     APIEndPoint.prototype.resource = function (endPoint, data) {
45995                         var customAction = {
45996                             method: 'GET',
45997                             isArray: false
45998                         };
45999                         var execute = {
46000                             method: 'POST',
46001                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
46002                         };
46003                         return this.$resource(endPoint, {}, { execute: execute });
46004                     };
46005                     APIEndPoint.prototype.getOptionControlFile = function (command) {
46006                         var endPoint = '/api/v1/optionControlFile/' + command;
46007                         return this.resource(endPoint, {}).get();
46008                     };
46009                     APIEndPoint.prototype.getFiles = function (fileId) {
46010                         var endPoint = '/api/v1/workspace';
46011                         if (fileId) {
46012                             endPoint += '/' + fileId;
46013                         }
46014                         return this.resource(endPoint, {}).get();
46015                     };
46016                     APIEndPoint.prototype.getDirectories = function () {
46017                         var endPoint = '/api/v1/all/workspace/directory';
46018                         return this.resource(endPoint, {}).get();
46019                     };
46020                     APIEndPoint.prototype.getTags = function () {
46021                         var endPoint = '/api/v1/tagList';
46022                         return this.resource(endPoint, {}).get();
46023                     };
46024                     APIEndPoint.prototype.getCommands = function () {
46025                         var endPoint = '/api/v1/commandList';
46026                         return this.resource(endPoint, {}).get();
46027                     };
46028                     APIEndPoint.prototype.execute = function (data) {
46029                         var endPoint = '/api/v1/execution';
46030                         var fd = new FormData();
46031                         fd.append('data', data);
46032                         return this.$http.post(endPoint, fd, {
46033                             headers: { 'Content-Type': undefined },
46034                             transformRequest: angular.identity
46035                         });
46036                     };
46037                     return APIEndPoint;
46038                 })();
46039                 services.APIEndPoint = APIEndPoint;
46040             })(services = app.services || (app.services = {}));
46041         })(app || (app = {}));
46042         var app;
46043         (function (app) {
46044             var services;
46045             (function (services) {
46046                 var MyModal = (function () {
46047                     function MyModal($uibModal) {
46048                         this.$uibModal = $uibModal;
46049                         this.modalOption = {
46050                             backdrop: true,
46051                             controller: null,
46052                             templateUrl: null,
46053                             size: null
46054                         };
46055                     }
46056                     MyModal.prototype.open = function (modalName) {
46057                         if (modalName === 'SelectCommand') {
46058                             this.modalOption.templateUrl = 'templates/select-command.html';
46059                             this.modalOption.size = 'lg';
46060                         }
46061                         return this.$uibModal.open(this.modalOption);
46062                     };
46063                     MyModal.prototype.selectCommand = function () {
46064                         this.modalOption.templateUrl = 'templates/select-command.html';
46065                         this.modalOption.controller = 'selectCommandController';
46066                         this.modalOption.controllerAs = 'c';
46067                         this.modalOption.size = 'lg';
46068                         return this.$uibModal.open(this.modalOption);
46069                     };
46070                     return MyModal;
46071                 })();
46072                 services.MyModal = MyModal;
46073             })(services = app.services || (app.services = {}));
46074         })(app || (app = {}));
46075         var app;
46076         (function (app) {
46077             var directives;
46078             (function (directives) {
46079                 var Command = (function () {
46080                     function Command() {
46081                         this.restrict = 'E';
46082                         this.replace = true;
46083                         this.scope = true;
46084                         this.controller = 'commandController';
46085                         this.controllerAs = 'ctrl';
46086                         this.bindToController = {
46087                             index: '=',
46088                             name: '=',
46089                             remove: '&',
46090                             list: '='
46091                         };
46092                         this.templateUrl = 'templates/command.html';
46093                     }
46094                     Command.Factory = function () {
46095                         var directive = function () {
46096                             return new Command();
46097                         };
46098                         directive.$inject = [];
46099                         return directive;
46100                     };
46101                     return Command;
46102                 })();
46103                 directives.Command = Command;
46104                 var CommandController = (function () {
46105                     function CommandController(APIEndPoint, $scope) {
46106                         this.APIEndPoint = APIEndPoint;
46107                         this.$scope = $scope;
46108                         var controller = this;
46109                         this.APIEndPoint
46110                             .getOptionControlFile('mrcImageNoiseAdd')
46111                             .$promise
46112                             .then(function (result) {
46113                             controller.options = result.info;
46114                         });
46115                         this.APIEndPoint
46116                             .getDirectories()
46117                             .$promise
46118                             .then(function (result) {
46119                             controller.dirs = result.info;
46120                         });
46121                         this.heading = "[" + this.index + "]: dcdFilePring";
46122                         this.isOpen = true;
46123                         this.$scope.$on('close', function () {
46124                             controller.isOpen = false;
46125                         });
46126                     }
46127                     CommandController.prototype.submit = function () {
46128                         var opt = [];
46129                         angular.forEach(this.options, function (option) {
46130                             var obj = {
46131                                 name: option.option,
46132                                 arguments: []
46133                             };
46134                             angular.forEach(option.arg, function (arg) {
46135                                 if (arg.input) {
46136                                     if (typeof arg.input === 'object') {
46137                                         obj.arguments.push(arg.input.name);
46138                                     }
46139                                     else {
46140                                         obj.arguments.push(arg.input);
46141                                     }
46142                                 }
46143                             });
46144                             if (obj.arguments.length > 0) {
46145                                 opt.push(obj);
46146                             }
46147                         });
46148                         var execObj = {
46149                             command: this.name,
46150                             workspace: this.workspace.fileId,
46151                             options: opt
46152                         };
46153                         this.APIEndPoint
46154                             .execute(JSON.stringify(execObj))
46155                             .then(function (result) {
46156                             console.log(result);
46157                         });
46158                     };
46159                     CommandController.prototype.removeMySelf = function (index) {
46160                         this.remove()(index, this.list);
46161                     };
46162                     CommandController.prototype.reloadFiles = function () {
46163                         var _this = this;
46164                         var fileId = this.workspace.fileId;
46165                         this.APIEndPoint
46166                             .getFiles(fileId)
46167                             .$promise
46168                             .then(function (result) {
46169                             var status = result.status;
46170                             if (status === 'success') {
46171                                 _this.files = result.info;
46172                             }
46173                             else {
46174                                 console.log(result.message);
46175                             }
46176                         });
46177                     };
46178                     CommandController.prototype.debug = function () {
46179                         console.log(this.files);
46180                         console.log(this.files);
46181                         console.log(this.workspace);
46182                     };
46183                     CommandController.$inject = ['APIEndPoint', '$scope'];
46184                     return CommandController;
46185                 })();
46186                 directives.CommandController = CommandController;
46187             })(directives = app.directives || (app.directives = {}));
46188         })(app || (app = {}));
46189         var app;
46190         (function (app) {
46191             var directives;
46192             (function (directives) {
46193                 var HeaderMenu = (function () {
46194                     function HeaderMenu() {
46195                         this.restrict = 'E';
46196                         this.replace = true;
46197                         this.templateUrl = 'templates/header-menu.html';
46198                     }
46199                     HeaderMenu.Factory = function () {
46200                         var directive = function () {
46201                             return new HeaderMenu();
46202                         };
46203                         return directive;
46204                     };
46205                     return HeaderMenu;
46206                 })();
46207                 directives.HeaderMenu = HeaderMenu;
46208             })(directives = app.directives || (app.directives = {}));
46209         })(app || (app = {}));
46210         var app;
46211         (function (app) {
46212             var directives;
46213             (function (directives) {
46214                 var Option = (function () {
46215                     function Option() {
46216                         this.restrict = 'E';
46217                         this.replace = true;
46218                         this.controller = 'optionController';
46219                         this.bindToController = {
46220                             info: '=',
46221                             files: '='
46222                         };
46223                         this.scope = true;
46224                         this.templateUrl = 'templates/option.html';
46225                         this.controllerAs = 'ctrl';
46226                     }
46227                     Option.Factory = function () {
46228                         var directive = function () {
46229                             return new Option();
46230                         };
46231                         directive.$inject = [];
46232                         return directive;
46233                     };
46234                     return Option;
46235                 })();
46236                 directives.Option = Option;
46237                 var OptionController = (function () {
46238                     function OptionController() {
46239                         var controller = this;
46240                         angular.forEach(controller.info.arg, function (arg) {
46241                             if (arg.initialValue) {
46242                                 if (arg.formType === 'number') {
46243                                     arg.input = parseInt(arg.initialValue);
46244                                 }
46245                                 else {
46246                                     arg.input = arg.initialValue;
46247                                 }
46248                             }
46249                         });
46250                     }
46251                     OptionController.$inject = [];
46252                     return OptionController;
46253                 })();
46254                 directives.OptionController = OptionController;
46255             })(directives = app.directives || (app.directives = {}));
46256         })(app || (app = {}));
46257         var app;
46258         (function (app) {
46259             var directives;
46260             (function (directives) {
46261                 var Directory = (function () {
46262                     function Directory() {
46263                         this.restrict = 'E';
46264                         this.replace = true;
46265                         this.controller = 'directoryController';
46266                         this.controllerAs = 'ctrl';
46267                         this.bindToController = {
46268                             info: '=',
46269                             add: '&',
46270                             list: '=',
46271                             files: '='
46272                         };
46273                         this.templateUrl = 'templates/directory.html';
46274                     }
46275                     Directory.Factory = function () {
46276                         var directive = function () {
46277                             return new Directory();
46278                         };
46279                         return directive;
46280                     };
46281                     return Directory;
46282                 })();
46283                 directives.Directory = Directory;
46284                 var DirectoryController = (function () {
46285                     function DirectoryController(APIEndPoint, $scope) {
46286                         this.APIEndPoint = APIEndPoint;
46287                         this.$scope = $scope;
46288                         var controller = this;
46289                         this.APIEndPoint
46290                             .getFiles(this.info.fileId)
46291                             .$promise
46292                             .then(function (result) {
46293                             if (result.status === 'success') {
46294                                 controller.files = result.info;
46295                                 angular.forEach(result.info, function (file) {
46296                                     if (file.fileType === '0') {
46297                                         var o = file;
46298                                         if (controller.info.path === '/') {
46299                                             o.path = '/' + file.name;
46300                                         }
46301                                         else {
46302                                             o.path = controller.info.path + '/' + file.name;
46303                                         }
46304                                         controller.add()(o, controller.list);
46305                                     }
46306                                 });
46307                             }
46308                             ;
46309                         });
46310                     }
46311                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
46312                     return DirectoryController;
46313                 })();
46314                 directives.DirectoryController = DirectoryController;
46315             })(directives = app.directives || (app.directives = {}));
46316         })(app || (app = {}));
46317         var app;
46318         (function (app) {
46319             var controllers;
46320             (function (controllers) {
46321                 var Execution = (function () {
46322                     function Execution(MyModal, $scope) {
46323                         this.MyModal = MyModal;
46324                         this.$scope = $scope;
46325                         this.commandInfoList = [];
46326                     }
46327                     ;
46328                     Execution.prototype.add = function () {
46329                         this.$scope.$broadcast('close');
46330                         this.MyModal.selectCommand();
46331                     };
46332                     Execution.prototype.open = function () {
46333                         var result = this.MyModal.open('SelectCommand');
46334                         console.log(result);
46335                     };
46336                     Execution.prototype.remove = function (index, list) {
46337                         list.splice(index, 1);
46338                     };
46339                     Execution.prototype.close = function () {
46340                         console.log("close");
46341                     };
46342                     Execution.$inject = ['MyModal', '$scope'];
46343                     return Execution;
46344                 })();
46345                 controllers.Execution = Execution;
46346             })(controllers = app.controllers || (app.controllers = {}));
46347         })(app || (app = {}));
46348         var app;
46349         (function (app) {
46350             var controllers;
46351             (function (controllers) {
46352                 var Workspace = (function () {
46353                     function Workspace($scope, APIEndPoint) {
46354                         this.$scope = $scope;
46355                         this.APIEndPoint = APIEndPoint;
46356                         this.directoryList = [];
46357                         var controller = this;
46358                         var directoryList = this.directoryList;
46359                         var o = {
46360                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
46361                             name: '',
46362                             parentId: '',
46363                             fileType: '',
46364                             createdAt: '',
46365                             updatedAt: '',
46366                             path: '/'
46367                         };
46368                         directoryList.push(o);
46369                     }
46370                     Workspace.prototype.addDirectory = function (info, directoryList) {
46371                         directoryList.push(info);
46372                     };
46373                     Workspace.$inject = ['$scope', 'APIEndPoint'];
46374                     return Workspace;
46375                 })();
46376                 controllers.Workspace = Workspace;
46377             })(controllers = app.controllers || (app.controllers = {}));
46378         })(app || (app = {}));
46379         var app;
46380         (function (app) {
46381             var controllers;
46382             (function (controllers) {
46383                 var History = (function () {
46384                     function History($scope) {
46385                         this.page = "History";
46386                     }
46387                     History.$inject = ['$scope'];
46388                     return History;
46389                 })();
46390                 controllers.History = History;
46391             })(controllers = app.controllers || (app.controllers = {}));
46392         })(app || (app = {}));
46393         var app;
46394         (function (app) {
46395             var controllers;
46396             (function (controllers) {
46397                 var SelectCommand = (function () {
46398                     function SelectCommand($scope, APIEndPoint) {
46399                         this.APIEndPoint = APIEndPoint;
46400                         var controller = this;
46401                         this.APIEndPoint
46402                             .getTags()
46403                             .$promise.then(function (result) {
46404                             controller.tags = result.info;
46405                         });
46406                         this.APIEndPoint
46407                             .getCommands()
46408                             .$promise.then(function (result) {
46409                             controller.commands = result.info;
46410                         });
46411                     }
46412                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
46413                     return SelectCommand;
46414                 })();
46415                 controllers.SelectCommand = SelectCommand;
46416             })(controllers = app.controllers || (app.controllers = {}));
46417         })(app || (app = {}));
46418         var app;
46419         (function (app) {
46420             'use strict';
46421             var appName = 'zephyr';
46422             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
46423             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
46424                 $urlRouterProvider.otherwise('/execution');
46425                 $locationProvider.html5Mode({
46426                     enabled: true,
46427                     requireBase: false
46428                 });
46429                 $stateProvider
46430                     .state('execution', {
46431                     url: '/execution',
46432                     templateUrl: 'templates/execution.html',
46433                     controller: 'executionController',
46434                     controllerAs: 'c'
46435                 })
46436                     .state('workspace', {
46437                     url: '/workspace',
46438                     templateUrl: 'templates/workspace.html',
46439                     controller: 'workspaceController',
46440                     controllerAs: 'c'
46441                 })
46442                     .state('history', {
46443                     url: '/history',
46444                     templateUrl: 'templates/history.html',
46445                     controller: 'historyController',
46446                     controllerAs: 'c'
46447                 });
46448             });
46449             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
46450             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
46451             app.zephyr.service('MyModal', app.services.MyModal);
46452             app.zephyr.controller('executionController', app.controllers.Execution);
46453             app.zephyr.controller('workspaceController', app.controllers.Workspace);
46454             app.zephyr.controller('historyController', app.controllers.History);
46455             app.zephyr.controller('commandController', app.directives.CommandController);
46456             app.zephyr.controller('optionController', app.directives.OptionController);
46457             app.zephyr.controller('directoryController', app.directives.DirectoryController);
46458             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
46459             app.zephyr.directive('command', app.directives.Command.Factory());
46460             app.zephyr.directive('option', app.directives.Option.Factory());
46461             app.zephyr.directive('directory', app.directives.Directory.Factory());
46462         })(app || (app = {}));
46463
46464
46465 /***/ },
46466 /* 19 */
46467 /***/ function(module, exports) {
46468
46469         var app;
46470         (function (app) {
46471             var declares;
46472             (function (declares) {
46473                 var CommandInfo = (function () {
46474                     function CommandInfo(name) {
46475                         this.name = name;
46476                     }
46477                     return CommandInfo;
46478                 })();
46479                 declares.CommandInfo = CommandInfo;
46480             })(declares = app.declares || (app.declares = {}));
46481         })(app || (app = {}));
46482         var app;
46483         (function (app) {
46484             var services;
46485             (function (services) {
46486                 var APIEndPoint = (function () {
46487                     function APIEndPoint($resource, $http) {
46488                         this.$resource = $resource;
46489                         this.$http = $http;
46490                     }
46491                     APIEndPoint.prototype.resource = function (endPoint, data) {
46492                         var customAction = {
46493                             method: 'GET',
46494                             isArray: false
46495                         };
46496                         var execute = {
46497                             method: 'POST',
46498                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
46499                         };
46500                         return this.$resource(endPoint, {}, { execute: execute });
46501                     };
46502                     APIEndPoint.prototype.getOptionControlFile = function (command) {
46503                         var endPoint = '/api/v1/optionControlFile/' + command;
46504                         return this.resource(endPoint, {}).get();
46505                     };
46506                     APIEndPoint.prototype.getFiles = function (fileId) {
46507                         var endPoint = '/api/v1/workspace';
46508                         if (fileId) {
46509                             endPoint += '/' + fileId;
46510                         }
46511                         return this.resource(endPoint, {}).get();
46512                     };
46513                     APIEndPoint.prototype.getDirectories = function () {
46514                         var endPoint = '/api/v1/all/workspace/directory';
46515                         return this.resource(endPoint, {}).get();
46516                     };
46517                     APIEndPoint.prototype.getTags = function () {
46518                         var endPoint = '/api/v1/tagList';
46519                         return this.resource(endPoint, {}).get();
46520                     };
46521                     APIEndPoint.prototype.getCommands = function () {
46522                         var endPoint = '/api/v1/commandList';
46523                         return this.resource(endPoint, {}).get();
46524                     };
46525                     APIEndPoint.prototype.execute = function (data) {
46526                         var endPoint = '/api/v1/execution';
46527                         var fd = new FormData();
46528                         fd.append('data', data);
46529                         return this.$http.post(endPoint, fd, {
46530                             headers: { 'Content-Type': undefined },
46531                             transformRequest: angular.identity
46532                         });
46533                     };
46534                     return APIEndPoint;
46535                 })();
46536                 services.APIEndPoint = APIEndPoint;
46537             })(services = app.services || (app.services = {}));
46538         })(app || (app = {}));
46539         var app;
46540         (function (app) {
46541             var services;
46542             (function (services) {
46543                 var MyModal = (function () {
46544                     function MyModal($uibModal) {
46545                         this.$uibModal = $uibModal;
46546                         this.modalOption = {
46547                             backdrop: true,
46548                             controller: null,
46549                             templateUrl: null,
46550                             size: null
46551                         };
46552                     }
46553                     MyModal.prototype.open = function (modalName) {
46554                         if (modalName === 'SelectCommand') {
46555                             this.modalOption.templateUrl = 'templates/select-command.html';
46556                             this.modalOption.size = 'lg';
46557                         }
46558                         return this.$uibModal.open(this.modalOption);
46559                     };
46560                     MyModal.prototype.selectCommand = function () {
46561                         this.modalOption.templateUrl = 'templates/select-command.html';
46562                         this.modalOption.controller = 'selectCommandController';
46563                         this.modalOption.controllerAs = 'c';
46564                         this.modalOption.size = 'lg';
46565                         return this.$uibModal.open(this.modalOption);
46566                     };
46567                     return MyModal;
46568                 })();
46569                 services.MyModal = MyModal;
46570             })(services = app.services || (app.services = {}));
46571         })(app || (app = {}));
46572         var app;
46573         (function (app) {
46574             var directives;
46575             (function (directives) {
46576                 var Command = (function () {
46577                     function Command() {
46578                         this.restrict = 'E';
46579                         this.replace = true;
46580                         this.scope = true;
46581                         this.controller = 'commandController';
46582                         this.controllerAs = 'ctrl';
46583                         this.bindToController = {
46584                             index: '=',
46585                             name: '=',
46586                             remove: '&',
46587                             list: '='
46588                         };
46589                         this.templateUrl = 'templates/command.html';
46590                     }
46591                     Command.Factory = function () {
46592                         var directive = function () {
46593                             return new Command();
46594                         };
46595                         directive.$inject = [];
46596                         return directive;
46597                     };
46598                     return Command;
46599                 })();
46600                 directives.Command = Command;
46601                 var CommandController = (function () {
46602                     function CommandController(APIEndPoint, $scope) {
46603                         this.APIEndPoint = APIEndPoint;
46604                         this.$scope = $scope;
46605                         var controller = this;
46606                         this.APIEndPoint
46607                             .getOptionControlFile('mrcImageNoiseAdd')
46608                             .$promise
46609                             .then(function (result) {
46610                             controller.options = result.info;
46611                         });
46612                         this.APIEndPoint
46613                             .getDirectories()
46614                             .$promise
46615                             .then(function (result) {
46616                             controller.dirs = result.info;
46617                         });
46618                         this.heading = "[" + this.index + "]: dcdFilePring";
46619                         this.isOpen = true;
46620                         this.$scope.$on('close', function () {
46621                             controller.isOpen = false;
46622                         });
46623                     }
46624                     CommandController.prototype.submit = function () {
46625                         var opt = [];
46626                         angular.forEach(this.options, function (option) {
46627                             var obj = {
46628                                 name: option.option,
46629                                 arguments: []
46630                             };
46631                             angular.forEach(option.arg, function (arg) {
46632                                 if (arg.input) {
46633                                     if (typeof arg.input === 'object') {
46634                                         obj.arguments.push(arg.input.name);
46635                                     }
46636                                     else {
46637                                         obj.arguments.push(arg.input);
46638                                     }
46639                                 }
46640                             });
46641                             if (obj.arguments.length > 0) {
46642                                 opt.push(obj);
46643                             }
46644                         });
46645                         var execObj = {
46646                             command: this.name,
46647                             workspace: this.workspace.fileId,
46648                             options: opt
46649                         };
46650                         this.APIEndPoint
46651                             .execute(JSON.stringify(execObj))
46652                             .then(function (result) {
46653                             console.log(result);
46654                         });
46655                     };
46656                     CommandController.prototype.removeMySelf = function (index) {
46657                         this.remove()(index, this.list);
46658                     };
46659                     CommandController.prototype.reloadFiles = function () {
46660                         var _this = this;
46661                         var fileId = this.workspace.fileId;
46662                         this.APIEndPoint
46663                             .getFiles(fileId)
46664                             .$promise
46665                             .then(function (result) {
46666                             var status = result.status;
46667                             if (status === 'success') {
46668                                 _this.files = result.info;
46669                             }
46670                             else {
46671                                 console.log(result.message);
46672                             }
46673                         });
46674                     };
46675                     CommandController.prototype.debug = function () {
46676                         console.log(this.files);
46677                         console.log(this.files);
46678                         console.log(this.workspace);
46679                     };
46680                     CommandController.$inject = ['APIEndPoint', '$scope'];
46681                     return CommandController;
46682                 })();
46683                 directives.CommandController = CommandController;
46684             })(directives = app.directives || (app.directives = {}));
46685         })(app || (app = {}));
46686         var app;
46687         (function (app) {
46688             var directives;
46689             (function (directives) {
46690                 var HeaderMenu = (function () {
46691                     function HeaderMenu() {
46692                         this.restrict = 'E';
46693                         this.replace = true;
46694                         this.templateUrl = 'templates/header-menu.html';
46695                     }
46696                     HeaderMenu.Factory = function () {
46697                         var directive = function () {
46698                             return new HeaderMenu();
46699                         };
46700                         return directive;
46701                     };
46702                     return HeaderMenu;
46703                 })();
46704                 directives.HeaderMenu = HeaderMenu;
46705             })(directives = app.directives || (app.directives = {}));
46706         })(app || (app = {}));
46707         var app;
46708         (function (app) {
46709             var directives;
46710             (function (directives) {
46711                 var Option = (function () {
46712                     function Option() {
46713                         this.restrict = 'E';
46714                         this.replace = true;
46715                         this.controller = 'optionController';
46716                         this.bindToController = {
46717                             info: '=',
46718                             files: '='
46719                         };
46720                         this.scope = true;
46721                         this.templateUrl = 'templates/option.html';
46722                         this.controllerAs = 'ctrl';
46723                     }
46724                     Option.Factory = function () {
46725                         var directive = function () {
46726                             return new Option();
46727                         };
46728                         directive.$inject = [];
46729                         return directive;
46730                     };
46731                     return Option;
46732                 })();
46733                 directives.Option = Option;
46734                 var OptionController = (function () {
46735                     function OptionController() {
46736                         var controller = this;
46737                         angular.forEach(controller.info.arg, function (arg) {
46738                             if (arg.initialValue) {
46739                                 if (arg.formType === 'number') {
46740                                     arg.input = parseInt(arg.initialValue);
46741                                 }
46742                                 else {
46743                                     arg.input = arg.initialValue;
46744                                 }
46745                             }
46746                         });
46747                     }
46748                     OptionController.$inject = [];
46749                     return OptionController;
46750                 })();
46751                 directives.OptionController = OptionController;
46752             })(directives = app.directives || (app.directives = {}));
46753         })(app || (app = {}));
46754         var app;
46755         (function (app) {
46756             var directives;
46757             (function (directives) {
46758                 var Directory = (function () {
46759                     function Directory() {
46760                         this.restrict = 'E';
46761                         this.replace = true;
46762                         this.controller = 'directoryController';
46763                         this.controllerAs = 'ctrl';
46764                         this.bindToController = {
46765                             info: '=',
46766                             add: '&',
46767                             list: '=',
46768                             files: '='
46769                         };
46770                         this.templateUrl = 'templates/directory.html';
46771                     }
46772                     Directory.Factory = function () {
46773                         var directive = function () {
46774                             return new Directory();
46775                         };
46776                         return directive;
46777                     };
46778                     return Directory;
46779                 })();
46780                 directives.Directory = Directory;
46781                 var DirectoryController = (function () {
46782                     function DirectoryController(APIEndPoint, $scope) {
46783                         this.APIEndPoint = APIEndPoint;
46784                         this.$scope = $scope;
46785                         var controller = this;
46786                         this.APIEndPoint
46787                             .getFiles(this.info.fileId)
46788                             .$promise
46789                             .then(function (result) {
46790                             if (result.status === 'success') {
46791                                 controller.files = result.info;
46792                                 angular.forEach(result.info, function (file) {
46793                                     if (file.fileType === '0') {
46794                                         var o = file;
46795                                         if (controller.info.path === '/') {
46796                                             o.path = '/' + file.name;
46797                                         }
46798                                         else {
46799                                             o.path = controller.info.path + '/' + file.name;
46800                                         }
46801                                         controller.add()(o, controller.list);
46802                                     }
46803                                 });
46804                             }
46805                             ;
46806                         });
46807                     }
46808                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
46809                     return DirectoryController;
46810                 })();
46811                 directives.DirectoryController = DirectoryController;
46812             })(directives = app.directives || (app.directives = {}));
46813         })(app || (app = {}));
46814         var app;
46815         (function (app) {
46816             var controllers;
46817             (function (controllers) {
46818                 var Execution = (function () {
46819                     function Execution(MyModal, $scope) {
46820                         this.MyModal = MyModal;
46821                         this.$scope = $scope;
46822                         this.commandInfoList = [];
46823                     }
46824                     ;
46825                     Execution.prototype.add = function () {
46826                         this.$scope.$broadcast('close');
46827                         this.MyModal.selectCommand();
46828                     };
46829                     Execution.prototype.open = function () {
46830                         var result = this.MyModal.open('SelectCommand');
46831                         console.log(result);
46832                     };
46833                     Execution.prototype.remove = function (index, list) {
46834                         list.splice(index, 1);
46835                     };
46836                     Execution.prototype.close = function () {
46837                         console.log("close");
46838                     };
46839                     Execution.$inject = ['MyModal', '$scope'];
46840                     return Execution;
46841                 })();
46842                 controllers.Execution = Execution;
46843             })(controllers = app.controllers || (app.controllers = {}));
46844         })(app || (app = {}));
46845         var app;
46846         (function (app) {
46847             var controllers;
46848             (function (controllers) {
46849                 var Workspace = (function () {
46850                     function Workspace($scope, APIEndPoint) {
46851                         this.$scope = $scope;
46852                         this.APIEndPoint = APIEndPoint;
46853                         this.directoryList = [];
46854                         var controller = this;
46855                         var directoryList = this.directoryList;
46856                         var o = {
46857                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
46858                             name: '',
46859                             parentId: '',
46860                             fileType: '',
46861                             createdAt: '',
46862                             updatedAt: '',
46863                             path: '/'
46864                         };
46865                         directoryList.push(o);
46866                     }
46867                     Workspace.prototype.addDirectory = function (info, directoryList) {
46868                         directoryList.push(info);
46869                     };
46870                     Workspace.$inject = ['$scope', 'APIEndPoint'];
46871                     return Workspace;
46872                 })();
46873                 controllers.Workspace = Workspace;
46874             })(controllers = app.controllers || (app.controllers = {}));
46875         })(app || (app = {}));
46876         var app;
46877         (function (app) {
46878             var controllers;
46879             (function (controllers) {
46880                 var History = (function () {
46881                     function History($scope) {
46882                         this.page = "History";
46883                     }
46884                     History.$inject = ['$scope'];
46885                     return History;
46886                 })();
46887                 controllers.History = History;
46888             })(controllers = app.controllers || (app.controllers = {}));
46889         })(app || (app = {}));
46890         var app;
46891         (function (app) {
46892             var controllers;
46893             (function (controllers) {
46894                 var SelectCommand = (function () {
46895                     function SelectCommand($scope, APIEndPoint) {
46896                         this.APIEndPoint = APIEndPoint;
46897                         var controller = this;
46898                         this.APIEndPoint
46899                             .getTags()
46900                             .$promise.then(function (result) {
46901                             controller.tags = result.info;
46902                         });
46903                         this.APIEndPoint
46904                             .getCommands()
46905                             .$promise.then(function (result) {
46906                             controller.commands = result.info;
46907                         });
46908                     }
46909                     SelectCommand.$inject = ['$scope', 'APIEndPoint'];
46910                     return SelectCommand;
46911                 })();
46912                 controllers.SelectCommand = SelectCommand;
46913             })(controllers = app.controllers || (app.controllers = {}));
46914         })(app || (app = {}));
46915         var app;
46916         (function (app) {
46917             'use strict';
46918             var appName = 'zephyr';
46919             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
46920             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
46921                 $urlRouterProvider.otherwise('/execution');
46922                 $locationProvider.html5Mode({
46923                     enabled: true,
46924                     requireBase: false
46925                 });
46926                 $stateProvider
46927                     .state('execution', {
46928                     url: '/execution',
46929                     templateUrl: 'templates/execution.html',
46930                     controller: 'executionController',
46931                     controllerAs: 'c'
46932                 })
46933                     .state('workspace', {
46934                     url: '/workspace',
46935                     templateUrl: 'templates/workspace.html',
46936                     controller: 'workspaceController',
46937                     controllerAs: 'c'
46938                 })
46939                     .state('history', {
46940                     url: '/history',
46941                     templateUrl: 'templates/history.html',
46942                     controller: 'historyController',
46943                     controllerAs: 'c'
46944                 });
46945             });
46946             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
46947             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
46948             app.zephyr.service('MyModal', app.services.MyModal);
46949             app.zephyr.controller('executionController', app.controllers.Execution);
46950             app.zephyr.controller('workspaceController', app.controllers.Workspace);
46951             app.zephyr.controller('historyController', app.controllers.History);
46952             app.zephyr.controller('commandController', app.directives.CommandController);
46953             app.zephyr.controller('optionController', app.directives.OptionController);
46954             app.zephyr.controller('directoryController', app.directives.DirectoryController);
46955             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
46956             app.zephyr.directive('command', app.directives.Command.Factory());
46957             app.zephyr.directive('option', app.directives.Option.Factory());
46958             app.zephyr.directive('directory', app.directives.Directory.Factory());
46959         })(app || (app = {}));
46960
46961
46962 /***/ }
46963 /******/ ]);