Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13542 anikendra 1
/**
2
 * @license AngularJS v1.3.6
3
 * (c) 2010-2014 Google, Inc. http://angularjs.org
4
 * License: MIT
5
 */
6
(function(window, document, undefined) {'use strict';
7
 
8
/**
9
 * @description
10
 *
11
 * This object provides a utility for producing rich Error messages within
12
 * Angular. It can be called as follows:
13
 *
14
 * var exampleMinErr = minErr('example');
15
 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16
 *
17
 * The above creates an instance of minErr in the example namespace. The
18
 * resulting error will have a namespaced error code of example.one.  The
19
 * resulting error will replace {0} with the value of foo, and {1} with the
20
 * value of bar. The object is not restricted in the number of arguments it can
21
 * take.
22
 *
23
 * If fewer arguments are specified than necessary for interpolation, the extra
24
 * interpolation markers will be preserved in the final string.
25
 *
26
 * Since data will be parsed statically during a build step, some restrictions
27
 * are applied with respect to how minErr instances are created and called.
28
 * Instances should have names of the form namespaceMinErr for a minErr created
29
 * using minErr('namespace') . Error codes, namespaces and template strings
30
 * should all be static strings, not variables or general expressions.
31
 *
32
 * @param {string} module The namespace to use for the new minErr instance.
33
 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34
 *   error from returned function, for cases when a particular type of error is useful.
35
 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36
 */
37
 
38
function minErr(module, ErrorConstructor) {
39
  ErrorConstructor = ErrorConstructor || Error;
40
  return function() {
41
    var code = arguments[0],
42
      prefix = '[' + (module ? module + ':' : '') + code + '] ',
43
      template = arguments[1],
44
      templateArgs = arguments,
45
 
46
      message, i;
47
 
48
    message = prefix + template.replace(/\{\d+\}/g, function(match) {
49
      var index = +match.slice(1, -1), arg;
50
 
51
      if (index + 2 < templateArgs.length) {
52
        return toDebugString(templateArgs[index + 2]);
53
      }
54
      return match;
55
    });
56
 
57
    message = message + '\nhttp://errors.angularjs.org/1.3.6/' +
58
      (module ? module + '/' : '') + code;
59
    for (i = 2; i < arguments.length; i++) {
60
      message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
61
        encodeURIComponent(toDebugString(arguments[i]));
62
    }
63
    return new ErrorConstructor(message);
64
  };
65
}
66
 
67
/* We need to tell jshint what variables are being exported */
68
/* global angular: true,
69
  msie: true,
70
  jqLite: true,
71
  jQuery: true,
72
  slice: true,
73
  splice: true,
74
  push: true,
75
  toString: true,
76
  ngMinErr: true,
77
  angularModule: true,
78
  uid: true,
79
  REGEX_STRING_REGEXP: true,
80
  VALIDITY_STATE_PROPERTY: true,
81
 
82
  lowercase: true,
83
  uppercase: true,
84
  manualLowercase: true,
85
  manualUppercase: true,
86
  nodeName_: true,
87
  isArrayLike: true,
88
  forEach: true,
89
  sortedKeys: true,
90
  forEachSorted: true,
91
  reverseParams: true,
92
  nextUid: true,
93
  setHashKey: true,
94
  extend: true,
95
  int: true,
96
  inherit: true,
97
  noop: true,
98
  identity: true,
99
  valueFn: true,
100
  isUndefined: true,
101
  isDefined: true,
102
  isObject: true,
103
  isString: true,
104
  isNumber: true,
105
  isDate: true,
106
  isArray: true,
107
  isFunction: true,
108
  isRegExp: true,
109
  isWindow: true,
110
  isScope: true,
111
  isFile: true,
112
  isBlob: true,
113
  isBoolean: true,
114
  isPromiseLike: true,
115
  trim: true,
116
  escapeForRegexp: true,
117
  isElement: true,
118
  makeMap: true,
119
  includes: true,
120
  arrayRemove: true,
121
  copy: true,
122
  shallowCopy: true,
123
  equals: true,
124
  csp: true,
125
  concat: true,
126
  sliceArgs: true,
127
  bind: true,
128
  toJsonReplacer: true,
129
  toJson: true,
130
  fromJson: true,
131
  startingTag: true,
132
  tryDecodeURIComponent: true,
133
  parseKeyValue: true,
134
  toKeyValue: true,
135
  encodeUriSegment: true,
136
  encodeUriQuery: true,
137
  angularInit: true,
138
  bootstrap: true,
139
  getTestability: true,
140
  snake_case: true,
141
  bindJQuery: true,
142
  assertArg: true,
143
  assertArgFn: true,
144
  assertNotHasOwnProperty: true,
145
  getter: true,
146
  getBlockNodes: true,
147
  hasOwnProperty: true,
148
  createMap: true,
149
 
150
  NODE_TYPE_ELEMENT: true,
151
  NODE_TYPE_TEXT: true,
152
  NODE_TYPE_COMMENT: true,
153
  NODE_TYPE_DOCUMENT: true,
154
  NODE_TYPE_DOCUMENT_FRAGMENT: true,
155
*/
156
 
157
////////////////////////////////////
158
 
159
/**
160
 * @ngdoc module
161
 * @name ng
162
 * @module ng
163
 * @description
164
 *
165
 * # ng (core module)
166
 * The ng module is loaded by default when an AngularJS application is started. The module itself
167
 * contains the essential components for an AngularJS application to function. The table below
168
 * lists a high level breakdown of each of the services/factories, filters, directives and testing
169
 * components available within this core module.
170
 *
171
 * <div doc-module-components="ng"></div>
172
 */
173
 
174
var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
175
 
176
// The name of a form control's ValidityState property.
177
// This is used so that it's possible for internal tests to create mock ValidityStates.
178
var VALIDITY_STATE_PROPERTY = 'validity';
179
 
180
/**
181
 * @ngdoc function
182
 * @name angular.lowercase
183
 * @module ng
184
 * @kind function
185
 *
186
 * @description Converts the specified string to lowercase.
187
 * @param {string} string String to be converted to lowercase.
188
 * @returns {string} Lowercased string.
189
 */
190
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
191
var hasOwnProperty = Object.prototype.hasOwnProperty;
192
 
193
/**
194
 * @ngdoc function
195
 * @name angular.uppercase
196
 * @module ng
197
 * @kind function
198
 *
199
 * @description Converts the specified string to uppercase.
200
 * @param {string} string String to be converted to uppercase.
201
 * @returns {string} Uppercased string.
202
 */
203
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
204
 
205
 
206
var manualLowercase = function(s) {
207
  /* jshint bitwise: false */
208
  return isString(s)
209
      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
210
      : s;
211
};
212
var manualUppercase = function(s) {
213
  /* jshint bitwise: false */
214
  return isString(s)
215
      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
216
      : s;
217
};
218
 
219
 
220
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
221
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
222
// with correct but slower alternatives.
223
if ('i' !== 'I'.toLowerCase()) {
224
  lowercase = manualLowercase;
225
  uppercase = manualUppercase;
226
}
227
 
228
 
229
var
230
    msie,             // holds major version number for IE, or NaN if UA is not IE.
231
    jqLite,           // delay binding since jQuery could be loaded after us.
232
    jQuery,           // delay binding
233
    slice             = [].slice,
234
    splice            = [].splice,
235
    push              = [].push,
236
    toString          = Object.prototype.toString,
237
    ngMinErr          = minErr('ng'),
238
 
239
    /** @name angular */
240
    angular           = window.angular || (window.angular = {}),
241
    angularModule,
242
    uid               = 0;
243
 
244
/**
245
 * documentMode is an IE-only property
246
 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
247
 */
248
msie = document.documentMode;
249
 
250
 
251
/**
252
 * @private
253
 * @param {*} obj
254
 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
255
 *                   String ...)
256
 */
257
function isArrayLike(obj) {
258
  if (obj == null || isWindow(obj)) {
259
    return false;
260
  }
261
 
262
  var length = obj.length;
263
 
264
  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
265
    return true;
266
  }
267
 
268
  return isString(obj) || isArray(obj) || length === 0 ||
269
         typeof length === 'number' && length > 0 && (length - 1) in obj;
270
}
271
 
272
/**
273
 * @ngdoc function
274
 * @name angular.forEach
275
 * @module ng
276
 * @kind function
277
 *
278
 * @description
279
 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
280
 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
281
 * is the value of an object property or an array element, `key` is the object property key or
282
 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
283
 *
284
 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
285
 * using the `hasOwnProperty` method.
286
 *
287
 * Unlike ES262's
288
 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
289
 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
290
 * return the value provided.
291
 *
292
   ```js
293
     var values = {name: 'misko', gender: 'male'};
294
     var log = [];
295
     angular.forEach(values, function(value, key) {
296
       this.push(key + ': ' + value);
297
     }, log);
298
     expect(log).toEqual(['name: misko', 'gender: male']);
299
   ```
300
 *
301
 * @param {Object|Array} obj Object to iterate over.
302
 * @param {Function} iterator Iterator function.
303
 * @param {Object=} context Object to become context (`this`) for the iterator function.
304
 * @returns {Object|Array} Reference to `obj`.
305
 */
306
 
307
function forEach(obj, iterator, context) {
308
  var key, length;
309
  if (obj) {
310
    if (isFunction(obj)) {
311
      for (key in obj) {
312
        // Need to check if hasOwnProperty exists,
313
        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
314
        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
315
          iterator.call(context, obj[key], key, obj);
316
        }
317
      }
318
    } else if (isArray(obj) || isArrayLike(obj)) {
319
      var isPrimitive = typeof obj !== 'object';
320
      for (key = 0, length = obj.length; key < length; key++) {
321
        if (isPrimitive || key in obj) {
322
          iterator.call(context, obj[key], key, obj);
323
        }
324
      }
325
    } else if (obj.forEach && obj.forEach !== forEach) {
326
        obj.forEach(iterator, context, obj);
327
    } else {
328
      for (key in obj) {
329
        if (obj.hasOwnProperty(key)) {
330
          iterator.call(context, obj[key], key, obj);
331
        }
332
      }
333
    }
334
  }
335
  return obj;
336
}
337
 
338
function sortedKeys(obj) {
339
  return Object.keys(obj).sort();
340
}
341
 
342
function forEachSorted(obj, iterator, context) {
343
  var keys = sortedKeys(obj);
344
  for (var i = 0; i < keys.length; i++) {
345
    iterator.call(context, obj[keys[i]], keys[i]);
346
  }
347
  return keys;
348
}
349
 
350
 
351
/**
352
 * when using forEach the params are value, key, but it is often useful to have key, value.
353
 * @param {function(string, *)} iteratorFn
354
 * @returns {function(*, string)}
355
 */
356
function reverseParams(iteratorFn) {
357
  return function(value, key) { iteratorFn(key, value); };
358
}
359
 
360
/**
361
 * A consistent way of creating unique IDs in angular.
362
 *
363
 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
364
 * we hit number precision issues in JavaScript.
365
 *
366
 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
367
 *
368
 * @returns {number} an unique alpha-numeric string
369
 */
370
function nextUid() {
371
  return ++uid;
372
}
373
 
374
 
375
/**
376
 * Set or clear the hashkey for an object.
377
 * @param obj object
378
 * @param h the hashkey (!truthy to delete the hashkey)
379
 */
380
function setHashKey(obj, h) {
381
  if (h) {
382
    obj.$$hashKey = h;
383
  }
384
  else {
385
    delete obj.$$hashKey;
386
  }
387
}
388
 
389
/**
390
 * @ngdoc function
391
 * @name angular.extend
392
 * @module ng
393
 * @kind function
394
 *
395
 * @description
396
 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
397
 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
398
 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
399
 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
400
 *
401
 * @param {Object} dst Destination object.
402
 * @param {...Object} src Source object(s).
403
 * @returns {Object} Reference to `dst`.
404
 */
405
function extend(dst) {
406
  var h = dst.$$hashKey;
407
 
408
  for (var i = 1, ii = arguments.length; i < ii; i++) {
409
    var obj = arguments[i];
410
    if (obj) {
411
      var keys = Object.keys(obj);
412
      for (var j = 0, jj = keys.length; j < jj; j++) {
413
        var key = keys[j];
414
        dst[key] = obj[key];
415
      }
416
    }
417
  }
418
 
419
  setHashKey(dst, h);
420
  return dst;
421
}
422
 
423
function int(str) {
424
  return parseInt(str, 10);
425
}
426
 
427
 
428
function inherit(parent, extra) {
429
  return extend(Object.create(parent), extra);
430
}
431
 
432
/**
433
 * @ngdoc function
434
 * @name angular.noop
435
 * @module ng
436
 * @kind function
437
 *
438
 * @description
439
 * A function that performs no operations. This function can be useful when writing code in the
440
 * functional style.
441
   ```js
442
     function foo(callback) {
443
       var result = calculateResult();
444
       (callback || angular.noop)(result);
445
     }
446
   ```
447
 */
448
function noop() {}
449
noop.$inject = [];
450
 
451
 
452
/**
453
 * @ngdoc function
454
 * @name angular.identity
455
 * @module ng
456
 * @kind function
457
 *
458
 * @description
459
 * A function that returns its first argument. This function is useful when writing code in the
460
 * functional style.
461
 *
462
   ```js
463
     function transformer(transformationFn, value) {
464
       return (transformationFn || angular.identity)(value);
465
     };
466
   ```
467
 */
468
function identity($) {return $;}
469
identity.$inject = [];
470
 
471
 
472
function valueFn(value) {return function() {return value;};}
473
 
474
/**
475
 * @ngdoc function
476
 * @name angular.isUndefined
477
 * @module ng
478
 * @kind function
479
 *
480
 * @description
481
 * Determines if a reference is undefined.
482
 *
483
 * @param {*} value Reference to check.
484
 * @returns {boolean} True if `value` is undefined.
485
 */
486
function isUndefined(value) {return typeof value === 'undefined';}
487
 
488
 
489
/**
490
 * @ngdoc function
491
 * @name angular.isDefined
492
 * @module ng
493
 * @kind function
494
 *
495
 * @description
496
 * Determines if a reference is defined.
497
 *
498
 * @param {*} value Reference to check.
499
 * @returns {boolean} True if `value` is defined.
500
 */
501
function isDefined(value) {return typeof value !== 'undefined';}
502
 
503
 
504
/**
505
 * @ngdoc function
506
 * @name angular.isObject
507
 * @module ng
508
 * @kind function
509
 *
510
 * @description
511
 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
512
 * considered to be objects. Note that JavaScript arrays are objects.
513
 *
514
 * @param {*} value Reference to check.
515
 * @returns {boolean} True if `value` is an `Object` but not `null`.
516
 */
517
function isObject(value) {
518
  // http://jsperf.com/isobject4
519
  return value !== null && typeof value === 'object';
520
}
521
 
522
 
523
/**
524
 * @ngdoc function
525
 * @name angular.isString
526
 * @module ng
527
 * @kind function
528
 *
529
 * @description
530
 * Determines if a reference is a `String`.
531
 *
532
 * @param {*} value Reference to check.
533
 * @returns {boolean} True if `value` is a `String`.
534
 */
535
function isString(value) {return typeof value === 'string';}
536
 
537
 
538
/**
539
 * @ngdoc function
540
 * @name angular.isNumber
541
 * @module ng
542
 * @kind function
543
 *
544
 * @description
545
 * Determines if a reference is a `Number`.
546
 *
547
 * @param {*} value Reference to check.
548
 * @returns {boolean} True if `value` is a `Number`.
549
 */
550
function isNumber(value) {return typeof value === 'number';}
551
 
552
 
553
/**
554
 * @ngdoc function
555
 * @name angular.isDate
556
 * @module ng
557
 * @kind function
558
 *
559
 * @description
560
 * Determines if a value is a date.
561
 *
562
 * @param {*} value Reference to check.
563
 * @returns {boolean} True if `value` is a `Date`.
564
 */
565
function isDate(value) {
566
  return toString.call(value) === '[object Date]';
567
}
568
 
569
 
570
/**
571
 * @ngdoc function
572
 * @name angular.isArray
573
 * @module ng
574
 * @kind function
575
 *
576
 * @description
577
 * Determines if a reference is an `Array`.
578
 *
579
 * @param {*} value Reference to check.
580
 * @returns {boolean} True if `value` is an `Array`.
581
 */
582
var isArray = Array.isArray;
583
 
584
/**
585
 * @ngdoc function
586
 * @name angular.isFunction
587
 * @module ng
588
 * @kind function
589
 *
590
 * @description
591
 * Determines if a reference is a `Function`.
592
 *
593
 * @param {*} value Reference to check.
594
 * @returns {boolean} True if `value` is a `Function`.
595
 */
596
function isFunction(value) {return typeof value === 'function';}
597
 
598
 
599
/**
600
 * Determines if a value is a regular expression object.
601
 *
602
 * @private
603
 * @param {*} value Reference to check.
604
 * @returns {boolean} True if `value` is a `RegExp`.
605
 */
606
function isRegExp(value) {
607
  return toString.call(value) === '[object RegExp]';
608
}
609
 
610
 
611
/**
612
 * Checks if `obj` is a window object.
613
 *
614
 * @private
615
 * @param {*} obj Object to check
616
 * @returns {boolean} True if `obj` is a window obj.
617
 */
618
function isWindow(obj) {
619
  return obj && obj.window === obj;
620
}
621
 
622
 
623
function isScope(obj) {
624
  return obj && obj.$evalAsync && obj.$watch;
625
}
626
 
627
 
628
function isFile(obj) {
629
  return toString.call(obj) === '[object File]';
630
}
631
 
632
 
633
function isBlob(obj) {
634
  return toString.call(obj) === '[object Blob]';
635
}
636
 
637
 
638
function isBoolean(value) {
639
  return typeof value === 'boolean';
640
}
641
 
642
 
643
function isPromiseLike(obj) {
644
  return obj && isFunction(obj.then);
645
}
646
 
647
 
648
var trim = function(value) {
649
  return isString(value) ? value.trim() : value;
650
};
651
 
652
// Copied from:
653
// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
654
// Prereq: s is a string.
655
var escapeForRegexp = function(s) {
656
  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
657
           replace(/\x08/g, '\\x08');
658
};
659
 
660
 
661
/**
662
 * @ngdoc function
663
 * @name angular.isElement
664
 * @module ng
665
 * @kind function
666
 *
667
 * @description
668
 * Determines if a reference is a DOM element (or wrapped jQuery element).
669
 *
670
 * @param {*} value Reference to check.
671
 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
672
 */
673
function isElement(node) {
674
  return !!(node &&
675
    (node.nodeName  // we are a direct element
676
    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
677
}
678
 
679
/**
680
 * @param str 'key1,key2,...'
681
 * @returns {object} in the form of {key1:true, key2:true, ...}
682
 */
683
function makeMap(str) {
684
  var obj = {}, items = str.split(","), i;
685
  for (i = 0; i < items.length; i++)
686
    obj[ items[i] ] = true;
687
  return obj;
688
}
689
 
690
 
691
function nodeName_(element) {
692
  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
693
}
694
 
695
function includes(array, obj) {
696
  return Array.prototype.indexOf.call(array, obj) != -1;
697
}
698
 
699
function arrayRemove(array, value) {
700
  var index = array.indexOf(value);
701
  if (index >= 0)
702
    array.splice(index, 1);
703
  return value;
704
}
705
 
706
/**
707
 * @ngdoc function
708
 * @name angular.copy
709
 * @module ng
710
 * @kind function
711
 *
712
 * @description
713
 * Creates a deep copy of `source`, which should be an object or an array.
714
 *
715
 * * If no destination is supplied, a copy of the object or array is created.
716
 * * If a destination is provided, all of its elements (for array) or properties (for objects)
717
 *   are deleted and then all elements/properties from the source are copied to it.
718
 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
719
 * * If `source` is identical to 'destination' an exception will be thrown.
720
 *
721
 * @param {*} source The source that will be used to make a copy.
722
 *                   Can be any type, including primitives, `null`, and `undefined`.
723
 * @param {(Object|Array)=} destination Destination into which the source is copied. If
724
 *     provided, must be of the same type as `source`.
725
 * @returns {*} The copy or updated `destination`, if `destination` was specified.
726
 *
727
 * @example
728
 <example module="copyExample">
729
 <file name="index.html">
730
 <div ng-controller="ExampleController">
731
 <form novalidate class="simple-form">
732
 Name: <input type="text" ng-model="user.name" /><br />
733
 E-mail: <input type="email" ng-model="user.email" /><br />
734
 Gender: <input type="radio" ng-model="user.gender" value="male" />male
735
 <input type="radio" ng-model="user.gender" value="female" />female<br />
736
 <button ng-click="reset()">RESET</button>
737
 <button ng-click="update(user)">SAVE</button>
738
 </form>
739
 <pre>form = {{user | json}}</pre>
740
 <pre>master = {{master | json}}</pre>
741
 </div>
742
 
743
 <script>
744
  angular.module('copyExample', [])
745
    .controller('ExampleController', ['$scope', function($scope) {
746
      $scope.master= {};
747
 
748
      $scope.update = function(user) {
749
        // Example with 1 argument
750
        $scope.master= angular.copy(user);
751
      };
752
 
753
      $scope.reset = function() {
754
        // Example with 2 arguments
755
        angular.copy($scope.master, $scope.user);
756
      };
757
 
758
      $scope.reset();
759
    }]);
760
 </script>
761
 </file>
762
 </example>
763
 */
764
function copy(source, destination, stackSource, stackDest) {
765
  if (isWindow(source) || isScope(source)) {
766
    throw ngMinErr('cpws',
767
      "Can't copy! Making copies of Window or Scope instances is not supported.");
768
  }
769
 
770
  if (!destination) {
771
    destination = source;
772
    if (source) {
773
      if (isArray(source)) {
774
        destination = copy(source, [], stackSource, stackDest);
775
      } else if (isDate(source)) {
776
        destination = new Date(source.getTime());
777
      } else if (isRegExp(source)) {
778
        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
779
        destination.lastIndex = source.lastIndex;
780
      } else if (isObject(source)) {
781
        var emptyObject = Object.create(Object.getPrototypeOf(source));
782
        destination = copy(source, emptyObject, stackSource, stackDest);
783
      }
784
    }
785
  } else {
786
    if (source === destination) throw ngMinErr('cpi',
787
      "Can't copy! Source and destination are identical.");
788
 
789
    stackSource = stackSource || [];
790
    stackDest = stackDest || [];
791
 
792
    if (isObject(source)) {
793
      var index = stackSource.indexOf(source);
794
      if (index !== -1) return stackDest[index];
795
 
796
      stackSource.push(source);
797
      stackDest.push(destination);
798
    }
799
 
800
    var result;
801
    if (isArray(source)) {
802
      destination.length = 0;
803
      for (var i = 0; i < source.length; i++) {
804
        result = copy(source[i], null, stackSource, stackDest);
805
        if (isObject(source[i])) {
806
          stackSource.push(source[i]);
807
          stackDest.push(result);
808
        }
809
        destination.push(result);
810
      }
811
    } else {
812
      var h = destination.$$hashKey;
813
      if (isArray(destination)) {
814
        destination.length = 0;
815
      } else {
816
        forEach(destination, function(value, key) {
817
          delete destination[key];
818
        });
819
      }
820
      for (var key in source) {
821
        if (source.hasOwnProperty(key)) {
822
          result = copy(source[key], null, stackSource, stackDest);
823
          if (isObject(source[key])) {
824
            stackSource.push(source[key]);
825
            stackDest.push(result);
826
          }
827
          destination[key] = result;
828
        }
829
      }
830
      setHashKey(destination,h);
831
    }
832
 
833
  }
834
  return destination;
835
}
836
 
837
/**
838
 * Creates a shallow copy of an object, an array or a primitive.
839
 *
840
 * Assumes that there are no proto properties for objects.
841
 */
842
function shallowCopy(src, dst) {
843
  if (isArray(src)) {
844
    dst = dst || [];
845
 
846
    for (var i = 0, ii = src.length; i < ii; i++) {
847
      dst[i] = src[i];
848
    }
849
  } else if (isObject(src)) {
850
    dst = dst || {};
851
 
852
    for (var key in src) {
853
      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
854
        dst[key] = src[key];
855
      }
856
    }
857
  }
858
 
859
  return dst || src;
860
}
861
 
862
 
863
/**
864
 * @ngdoc function
865
 * @name angular.equals
866
 * @module ng
867
 * @kind function
868
 *
869
 * @description
870
 * Determines if two objects or two values are equivalent. Supports value types, regular
871
 * expressions, arrays and objects.
872
 *
873
 * Two objects or values are considered equivalent if at least one of the following is true:
874
 *
875
 * * Both objects or values pass `===` comparison.
876
 * * Both objects or values are of the same type and all of their properties are equal by
877
 *   comparing them with `angular.equals`.
878
 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
879
 * * Both values represent the same regular expression (In JavaScript,
880
 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
881
 *   representation matches).
882
 *
883
 * During a property comparison, properties of `function` type and properties with names
884
 * that begin with `$` are ignored.
885
 *
886
 * Scope and DOMWindow objects are being compared only by identify (`===`).
887
 *
888
 * @param {*} o1 Object or value to compare.
889
 * @param {*} o2 Object or value to compare.
890
 * @returns {boolean} True if arguments are equal.
891
 */
892
function equals(o1, o2) {
893
  if (o1 === o2) return true;
894
  if (o1 === null || o2 === null) return false;
895
  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
896
  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
897
  if (t1 == t2) {
898
    if (t1 == 'object') {
899
      if (isArray(o1)) {
900
        if (!isArray(o2)) return false;
901
        if ((length = o1.length) == o2.length) {
902
          for (key = 0; key < length; key++) {
903
            if (!equals(o1[key], o2[key])) return false;
904
          }
905
          return true;
906
        }
907
      } else if (isDate(o1)) {
908
        if (!isDate(o2)) return false;
909
        return equals(o1.getTime(), o2.getTime());
910
      } else if (isRegExp(o1) && isRegExp(o2)) {
911
        return o1.toString() == o2.toString();
912
      } else {
913
        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
914
        keySet = {};
915
        for (key in o1) {
916
          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
917
          if (!equals(o1[key], o2[key])) return false;
918
          keySet[key] = true;
919
        }
920
        for (key in o2) {
921
          if (!keySet.hasOwnProperty(key) &&
922
              key.charAt(0) !== '$' &&
923
              o2[key] !== undefined &&
924
              !isFunction(o2[key])) return false;
925
        }
926
        return true;
927
      }
928
    }
929
  }
930
  return false;
931
}
932
 
933
var csp = function() {
934
  if (isDefined(csp.isActive_)) return csp.isActive_;
935
 
936
  var active = !!(document.querySelector('[ng-csp]') ||
937
                  document.querySelector('[data-ng-csp]'));
938
 
939
  if (!active) {
940
    try {
941
      /* jshint -W031, -W054 */
942
      new Function('');
943
      /* jshint +W031, +W054 */
944
    } catch (e) {
945
      active = true;
946
    }
947
  }
948
 
949
  return (csp.isActive_ = active);
950
};
951
 
952
 
953
 
954
function concat(array1, array2, index) {
955
  return array1.concat(slice.call(array2, index));
956
}
957
 
958
function sliceArgs(args, startIndex) {
959
  return slice.call(args, startIndex || 0);
960
}
961
 
962
 
963
/* jshint -W101 */
964
/**
965
 * @ngdoc function
966
 * @name angular.bind
967
 * @module ng
968
 * @kind function
969
 *
970
 * @description
971
 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
972
 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
973
 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
974
 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
975
 *
976
 * @param {Object} self Context which `fn` should be evaluated in.
977
 * @param {function()} fn Function to be bound.
978
 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
979
 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
980
 */
981
/* jshint +W101 */
982
function bind(self, fn) {
983
  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
984
  if (isFunction(fn) && !(fn instanceof RegExp)) {
985
    return curryArgs.length
986
      ? function() {
987
          return arguments.length
988
            ? fn.apply(self, concat(curryArgs, arguments, 0))
989
            : fn.apply(self, curryArgs);
990
        }
991
      : function() {
992
          return arguments.length
993
            ? fn.apply(self, arguments)
994
            : fn.call(self);
995
        };
996
  } else {
997
    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
998
    return fn;
999
  }
1000
}
1001
 
1002
 
1003
function toJsonReplacer(key, value) {
1004
  var val = value;
1005
 
1006
  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1007
    val = undefined;
1008
  } else if (isWindow(value)) {
1009
    val = '$WINDOW';
1010
  } else if (value &&  document === value) {
1011
    val = '$DOCUMENT';
1012
  } else if (isScope(value)) {
1013
    val = '$SCOPE';
1014
  }
1015
 
1016
  return val;
1017
}
1018
 
1019
 
1020
/**
1021
 * @ngdoc function
1022
 * @name angular.toJson
1023
 * @module ng
1024
 * @kind function
1025
 *
1026
 * @description
1027
 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1028
 * stripped since angular uses this notation internally.
1029
 *
1030
 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1031
 * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.
1032
 *    If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).
1033
 * @returns {string|undefined} JSON-ified string representing `obj`.
1034
 */
1035
function toJson(obj, pretty) {
1036
  if (typeof obj === 'undefined') return undefined;
1037
  if (!isNumber(pretty)) {
1038
    pretty = pretty ? 2 : null;
1039
  }
1040
  return JSON.stringify(obj, toJsonReplacer, pretty);
1041
}
1042
 
1043
 
1044
/**
1045
 * @ngdoc function
1046
 * @name angular.fromJson
1047
 * @module ng
1048
 * @kind function
1049
 *
1050
 * @description
1051
 * Deserializes a JSON string.
1052
 *
1053
 * @param {string} json JSON string to deserialize.
1054
 * @returns {Object|Array|string|number} Deserialized thingy.
1055
 */
1056
function fromJson(json) {
1057
  return isString(json)
1058
      ? JSON.parse(json)
1059
      : json;
1060
}
1061
 
1062
 
1063
/**
1064
 * @returns {string} Returns the string representation of the element.
1065
 */
1066
function startingTag(element) {
1067
  element = jqLite(element).clone();
1068
  try {
1069
    // turns out IE does not let you set .html() on elements which
1070
    // are not allowed to have children. So we just ignore it.
1071
    element.empty();
1072
  } catch (e) {}
1073
  var elemHtml = jqLite('<div>').append(element).html();
1074
  try {
1075
    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1076
        elemHtml.
1077
          match(/^(<[^>]+>)/)[1].
1078
          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1079
  } catch (e) {
1080
    return lowercase(elemHtml);
1081
  }
1082
 
1083
}
1084
 
1085
 
1086
/////////////////////////////////////////////////
1087
 
1088
/**
1089
 * Tries to decode the URI component without throwing an exception.
1090
 *
1091
 * @private
1092
 * @param str value potential URI component to check.
1093
 * @returns {boolean} True if `value` can be decoded
1094
 * with the decodeURIComponent function.
1095
 */
1096
function tryDecodeURIComponent(value) {
1097
  try {
1098
    return decodeURIComponent(value);
1099
  } catch (e) {
1100
    // Ignore any invalid uri component
1101
  }
1102
}
1103
 
1104
 
1105
/**
1106
 * Parses an escaped url query string into key-value pairs.
1107
 * @returns {Object.<string,boolean|Array>}
1108
 */
1109
function parseKeyValue(/**string*/keyValue) {
1110
  var obj = {}, key_value, key;
1111
  forEach((keyValue || "").split('&'), function(keyValue) {
1112
    if (keyValue) {
1113
      key_value = keyValue.replace(/\+/g,'%20').split('=');
1114
      key = tryDecodeURIComponent(key_value[0]);
1115
      if (isDefined(key)) {
1116
        var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1117
        if (!hasOwnProperty.call(obj, key)) {
1118
          obj[key] = val;
1119
        } else if (isArray(obj[key])) {
1120
          obj[key].push(val);
1121
        } else {
1122
          obj[key] = [obj[key],val];
1123
        }
1124
      }
1125
    }
1126
  });
1127
  return obj;
1128
}
1129
 
1130
function toKeyValue(obj) {
1131
  var parts = [];
1132
  forEach(obj, function(value, key) {
1133
    if (isArray(value)) {
1134
      forEach(value, function(arrayValue) {
1135
        parts.push(encodeUriQuery(key, true) +
1136
                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1137
      });
1138
    } else {
1139
    parts.push(encodeUriQuery(key, true) +
1140
               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1141
    }
1142
  });
1143
  return parts.length ? parts.join('&') : '';
1144
}
1145
 
1146
 
1147
/**
1148
 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1149
 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1150
 * segments:
1151
 *    segment       = *pchar
1152
 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1153
 *    pct-encoded   = "%" HEXDIG HEXDIG
1154
 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1155
 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1156
 *                     / "*" / "+" / "," / ";" / "="
1157
 */
1158
function encodeUriSegment(val) {
1159
  return encodeUriQuery(val, true).
1160
             replace(/%26/gi, '&').
1161
             replace(/%3D/gi, '=').
1162
             replace(/%2B/gi, '+');
1163
}
1164
 
1165
 
1166
/**
1167
 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1168
 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1169
 * encoded per http://tools.ietf.org/html/rfc3986:
1170
 *    query       = *( pchar / "/" / "?" )
1171
 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1172
 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1173
 *    pct-encoded   = "%" HEXDIG HEXDIG
1174
 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1175
 *                     / "*" / "+" / "," / ";" / "="
1176
 */
1177
function encodeUriQuery(val, pctEncodeSpaces) {
1178
  return encodeURIComponent(val).
1179
             replace(/%40/gi, '@').
1180
             replace(/%3A/gi, ':').
1181
             replace(/%24/g, '$').
1182
             replace(/%2C/gi, ',').
1183
             replace(/%3B/gi, ';').
1184
             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1185
}
1186
 
1187
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1188
 
1189
function getNgAttribute(element, ngAttr) {
1190
  var attr, i, ii = ngAttrPrefixes.length;
1191
  element = jqLite(element);
1192
  for (i = 0; i < ii; ++i) {
1193
    attr = ngAttrPrefixes[i] + ngAttr;
1194
    if (isString(attr = element.attr(attr))) {
1195
      return attr;
1196
    }
1197
  }
1198
  return null;
1199
}
1200
 
1201
/**
1202
 * @ngdoc directive
1203
 * @name ngApp
1204
 * @module ng
1205
 *
1206
 * @element ANY
1207
 * @param {angular.Module} ngApp an optional application
1208
 *   {@link angular.module module} name to load.
1209
 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1210
 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1211
 *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1212
 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1213
 *   tracking down the root of these bugs.
1214
 *
1215
 * @description
1216
 *
1217
 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1218
 * designates the **root element** of the application and is typically placed near the root element
1219
 * of the page - e.g. on the `<body>` or `<html>` tags.
1220
 *
1221
 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1222
 * found in the document will be used to define the root element to auto-bootstrap as an
1223
 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1224
 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1225
 *
1226
 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1227
 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
1228
 * should contain the application code needed or have dependencies on other modules that will
1229
 * contain the code. See {@link angular.module} for more information.
1230
 *
1231
 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1232
 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1233
 * would not be resolved to `3`.
1234
 *
1235
 * `ngApp` is the easiest, and most common, way to bootstrap an application.
1236
 *
1237
 <example module="ngAppDemo">
1238
   <file name="index.html">
1239
   <div ng-controller="ngAppDemoController">
1240
     I can add: {{a}} + {{b}} =  {{ a+b }}
1241
   </div>
1242
   </file>
1243
   <file name="script.js">
1244
   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1245
     $scope.a = 1;
1246
     $scope.b = 2;
1247
   });
1248
   </file>
1249
 </example>
1250
 *
1251
 * Using `ngStrictDi`, you would see something like this:
1252
 *
1253
 <example ng-app-included="true">
1254
   <file name="index.html">
1255
   <div ng-app="ngAppStrictDemo" ng-strict-di>
1256
       <div ng-controller="GoodController1">
1257
           I can add: {{a}} + {{b}} =  {{ a+b }}
1258
 
1259
           <p>This renders because the controller does not fail to
1260
              instantiate, by using explicit annotation style (see
1261
              script.js for details)
1262
           </p>
1263
       </div>
1264
 
1265
       <div ng-controller="GoodController2">
1266
           Name: <input ng-model="name"><br />
1267
           Hello, {{name}}!
1268
 
1269
           <p>This renders because the controller does not fail to
1270
              instantiate, by using explicit annotation style
1271
              (see script.js for details)
1272
           </p>
1273
       </div>
1274
 
1275
       <div ng-controller="BadController">
1276
           I can add: {{a}} + {{b}} =  {{ a+b }}
1277
 
1278
           <p>The controller could not be instantiated, due to relying
1279
              on automatic function annotations (which are disabled in
1280
              strict mode). As such, the content of this section is not
1281
              interpolated, and there should be an error in your web console.
1282
           </p>
1283
       </div>
1284
   </div>
1285
   </file>
1286
   <file name="script.js">
1287
   angular.module('ngAppStrictDemo', [])
1288
     // BadController will fail to instantiate, due to relying on automatic function annotation,
1289
     // rather than an explicit annotation
1290
     .controller('BadController', function($scope) {
1291
       $scope.a = 1;
1292
       $scope.b = 2;
1293
     })
1294
     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1295
     // due to using explicit annotations using the array style and $inject property, respectively.
1296
     .controller('GoodController1', ['$scope', function($scope) {
1297
       $scope.a = 1;
1298
       $scope.b = 2;
1299
     }])
1300
     .controller('GoodController2', GoodController2);
1301
     function GoodController2($scope) {
1302
       $scope.name = "World";
1303
     }
1304
     GoodController2.$inject = ['$scope'];
1305
   </file>
1306
   <file name="style.css">
1307
   div[ng-controller] {
1308
       margin-bottom: 1em;
1309
       -webkit-border-radius: 4px;
1310
       border-radius: 4px;
1311
       border: 1px solid;
1312
       padding: .5em;
1313
   }
1314
   div[ng-controller^=Good] {
1315
       border-color: #d6e9c6;
1316
       background-color: #dff0d8;
1317
       color: #3c763d;
1318
   }
1319
   div[ng-controller^=Bad] {
1320
       border-color: #ebccd1;
1321
       background-color: #f2dede;
1322
       color: #a94442;
1323
       margin-bottom: 0;
1324
   }
1325
   </file>
1326
 </example>
1327
 */
1328
function angularInit(element, bootstrap) {
1329
  var appElement,
1330
      module,
1331
      config = {};
1332
 
1333
  // The element `element` has priority over any other element
1334
  forEach(ngAttrPrefixes, function(prefix) {
1335
    var name = prefix + 'app';
1336
 
1337
    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1338
      appElement = element;
1339
      module = element.getAttribute(name);
1340
    }
1341
  });
1342
  forEach(ngAttrPrefixes, function(prefix) {
1343
    var name = prefix + 'app';
1344
    var candidate;
1345
 
1346
    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1347
      appElement = candidate;
1348
      module = candidate.getAttribute(name);
1349
    }
1350
  });
1351
  if (appElement) {
1352
    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1353
    bootstrap(appElement, module ? [module] : [], config);
1354
  }
1355
}
1356
 
1357
/**
1358
 * @ngdoc function
1359
 * @name angular.bootstrap
1360
 * @module ng
1361
 * @description
1362
 * Use this function to manually start up angular application.
1363
 *
1364
 * See: {@link guide/bootstrap Bootstrap}
1365
 *
1366
 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1367
 * They must use {@link ng.directive:ngApp ngApp}.
1368
 *
1369
 * Angular will detect if it has been loaded into the browser more than once and only allow the
1370
 * first loaded script to be bootstrapped and will report a warning to the browser console for
1371
 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1372
 * multiple instances of Angular try to work on the DOM.
1373
 *
1374
 * ```html
1375
 * <!doctype html>
1376
 * <html>
1377
 * <body>
1378
 * <div ng-controller="WelcomeController">
1379
 *   {{greeting}}
1380
 * </div>
1381
 *
1382
 * <script src="angular.js"></script>
1383
 * <script>
1384
 *   var app = angular.module('demo', [])
1385
 *   .controller('WelcomeController', function($scope) {
1386
 *       $scope.greeting = 'Welcome!';
1387
 *   });
1388
 *   angular.bootstrap(document, ['demo']);
1389
 * </script>
1390
 * </body>
1391
 * </html>
1392
 * ```
1393
 *
1394
 * @param {DOMElement} element DOM element which is the root of angular application.
1395
 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1396
 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1397
 *     function that will be invoked by the injector as a run block.
1398
 *     See: {@link angular.module modules}
1399
 * @param {Object=} config an object for defining configuration options for the application. The
1400
 *     following keys are supported:
1401
 *
1402
 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1403
 *   assist in finding bugs which break minified code. Defaults to `false`.
1404
 *
1405
 * @returns {auto.$injector} Returns the newly created injector for this app.
1406
 */
1407
function bootstrap(element, modules, config) {
1408
  if (!isObject(config)) config = {};
1409
  var defaultConfig = {
1410
    strictDi: false
1411
  };
1412
  config = extend(defaultConfig, config);
1413
  var doBootstrap = function() {
1414
    element = jqLite(element);
1415
 
1416
    if (element.injector()) {
1417
      var tag = (element[0] === document) ? 'document' : startingTag(element);
1418
      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1419
      throw ngMinErr(
1420
          'btstrpd',
1421
          "App Already Bootstrapped with this Element '{0}'",
1422
          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1423
    }
1424
 
1425
    modules = modules || [];
1426
    modules.unshift(['$provide', function($provide) {
1427
      $provide.value('$rootElement', element);
1428
    }]);
1429
 
1430
    if (config.debugInfoEnabled) {
1431
      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1432
      modules.push(['$compileProvider', function($compileProvider) {
1433
        $compileProvider.debugInfoEnabled(true);
1434
      }]);
1435
    }
1436
 
1437
    modules.unshift('ng');
1438
    var injector = createInjector(modules, config.strictDi);
1439
    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1440
       function bootstrapApply(scope, element, compile, injector) {
1441
        scope.$apply(function() {
1442
          element.data('$injector', injector);
1443
          compile(element)(scope);
1444
        });
1445
      }]
1446
    );
1447
    return injector;
1448
  };
1449
 
1450
  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1451
  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1452
 
1453
  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1454
    config.debugInfoEnabled = true;
1455
    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1456
  }
1457
 
1458
  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1459
    return doBootstrap();
1460
  }
1461
 
1462
  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1463
  angular.resumeBootstrap = function(extraModules) {
1464
    forEach(extraModules, function(module) {
1465
      modules.push(module);
1466
    });
1467
    doBootstrap();
1468
  };
1469
}
1470
 
1471
/**
1472
 * @ngdoc function
1473
 * @name angular.reloadWithDebugInfo
1474
 * @module ng
1475
 * @description
1476
 * Use this function to reload the current application with debug information turned on.
1477
 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1478
 *
1479
 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1480
 */
1481
function reloadWithDebugInfo() {
1482
  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1483
  window.location.reload();
1484
}
1485
 
1486
/**
1487
 * @name angular.getTestability
1488
 * @module ng
1489
 * @description
1490
 * Get the testability service for the instance of Angular on the given
1491
 * element.
1492
 * @param {DOMElement} element DOM element which is the root of angular application.
1493
 */
1494
function getTestability(rootElement) {
1495
  return angular.element(rootElement).injector().get('$$testability');
1496
}
1497
 
1498
var SNAKE_CASE_REGEXP = /[A-Z]/g;
1499
function snake_case(name, separator) {
1500
  separator = separator || '_';
1501
  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1502
    return (pos ? separator : '') + letter.toLowerCase();
1503
  });
1504
}
1505
 
1506
var bindJQueryFired = false;
1507
var skipDestroyOnNextJQueryCleanData;
1508
function bindJQuery() {
1509
  var originalCleanData;
1510
 
1511
  if (bindJQueryFired) {
1512
    return;
1513
  }
1514
 
1515
  // bind to jQuery if present;
1516
  jQuery = window.jQuery;
1517
  // Use jQuery if it exists with proper functionality, otherwise default to us.
1518
  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1519
  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1520
  // versions. It will not work for sure with jQuery <1.7, though.
1521
  if (jQuery && jQuery.fn.on) {
1522
    jqLite = jQuery;
1523
    extend(jQuery.fn, {
1524
      scope: JQLitePrototype.scope,
1525
      isolateScope: JQLitePrototype.isolateScope,
1526
      controller: JQLitePrototype.controller,
1527
      injector: JQLitePrototype.injector,
1528
      inheritedData: JQLitePrototype.inheritedData
1529
    });
1530
 
1531
    // All nodes removed from the DOM via various jQuery APIs like .remove()
1532
    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1533
    // the $destroy event on all removed nodes.
1534
    originalCleanData = jQuery.cleanData;
1535
    jQuery.cleanData = function(elems) {
1536
      var events;
1537
      if (!skipDestroyOnNextJQueryCleanData) {
1538
        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1539
          events = jQuery._data(elem, "events");
1540
          if (events && events.$destroy) {
1541
            jQuery(elem).triggerHandler('$destroy');
1542
          }
1543
        }
1544
      } else {
1545
        skipDestroyOnNextJQueryCleanData = false;
1546
      }
1547
      originalCleanData(elems);
1548
    };
1549
  } else {
1550
    jqLite = JQLite;
1551
  }
1552
 
1553
  angular.element = jqLite;
1554
 
1555
  // Prevent double-proxying.
1556
  bindJQueryFired = true;
1557
}
1558
 
1559
/**
1560
 * throw error if the argument is falsy.
1561
 */
1562
function assertArg(arg, name, reason) {
1563
  if (!arg) {
1564
    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1565
  }
1566
  return arg;
1567
}
1568
 
1569
function assertArgFn(arg, name, acceptArrayAnnotation) {
1570
  if (acceptArrayAnnotation && isArray(arg)) {
1571
      arg = arg[arg.length - 1];
1572
  }
1573
 
1574
  assertArg(isFunction(arg), name, 'not a function, got ' +
1575
      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1576
  return arg;
1577
}
1578
 
1579
/**
1580
 * throw error if the name given is hasOwnProperty
1581
 * @param  {String} name    the name to test
1582
 * @param  {String} context the context in which the name is used, such as module or directive
1583
 */
1584
function assertNotHasOwnProperty(name, context) {
1585
  if (name === 'hasOwnProperty') {
1586
    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1587
  }
1588
}
1589
 
1590
/**
1591
 * Return the value accessible from the object by path. Any undefined traversals are ignored
1592
 * @param {Object} obj starting object
1593
 * @param {String} path path to traverse
1594
 * @param {boolean} [bindFnToScope=true]
1595
 * @returns {Object} value as accessible by path
1596
 */
1597
//TODO(misko): this function needs to be removed
1598
function getter(obj, path, bindFnToScope) {
1599
  if (!path) return obj;
1600
  var keys = path.split('.');
1601
  var key;
1602
  var lastInstance = obj;
1603
  var len = keys.length;
1604
 
1605
  for (var i = 0; i < len; i++) {
1606
    key = keys[i];
1607
    if (obj) {
1608
      obj = (lastInstance = obj)[key];
1609
    }
1610
  }
1611
  if (!bindFnToScope && isFunction(obj)) {
1612
    return bind(lastInstance, obj);
1613
  }
1614
  return obj;
1615
}
1616
 
1617
/**
1618
 * Return the DOM siblings between the first and last node in the given array.
1619
 * @param {Array} array like object
1620
 * @returns {jqLite} jqLite collection containing the nodes
1621
 */
1622
function getBlockNodes(nodes) {
1623
  // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1624
  //             collection, otherwise update the original collection.
1625
  var node = nodes[0];
1626
  var endNode = nodes[nodes.length - 1];
1627
  var blockNodes = [node];
1628
 
1629
  do {
1630
    node = node.nextSibling;
1631
    if (!node) break;
1632
    blockNodes.push(node);
1633
  } while (node !== endNode);
1634
 
1635
  return jqLite(blockNodes);
1636
}
1637
 
1638
 
1639
/**
1640
 * Creates a new object without a prototype. This object is useful for lookup without having to
1641
 * guard against prototypically inherited properties via hasOwnProperty.
1642
 *
1643
 * Related micro-benchmarks:
1644
 * - http://jsperf.com/object-create2
1645
 * - http://jsperf.com/proto-map-lookup/2
1646
 * - http://jsperf.com/for-in-vs-object-keys2
1647
 *
1648
 * @returns {Object}
1649
 */
1650
function createMap() {
1651
  return Object.create(null);
1652
}
1653
 
1654
var NODE_TYPE_ELEMENT = 1;
1655
var NODE_TYPE_TEXT = 3;
1656
var NODE_TYPE_COMMENT = 8;
1657
var NODE_TYPE_DOCUMENT = 9;
1658
var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1659
 
1660
/**
1661
 * @ngdoc type
1662
 * @name angular.Module
1663
 * @module ng
1664
 * @description
1665
 *
1666
 * Interface for configuring angular {@link angular.module modules}.
1667
 */
1668
 
1669
function setupModuleLoader(window) {
1670
 
1671
  var $injectorMinErr = minErr('$injector');
1672
  var ngMinErr = minErr('ng');
1673
 
1674
  function ensure(obj, name, factory) {
1675
    return obj[name] || (obj[name] = factory());
1676
  }
1677
 
1678
  var angular = ensure(window, 'angular', Object);
1679
 
1680
  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1681
  angular.$$minErr = angular.$$minErr || minErr;
1682
 
1683
  return ensure(angular, 'module', function() {
1684
    /** @type {Object.<string, angular.Module>} */
1685
    var modules = {};
1686
 
1687
    /**
1688
     * @ngdoc function
1689
     * @name angular.module
1690
     * @module ng
1691
     * @description
1692
     *
1693
     * The `angular.module` is a global place for creating, registering and retrieving Angular
1694
     * modules.
1695
     * All modules (angular core or 3rd party) that should be available to an application must be
1696
     * registered using this mechanism.
1697
     *
1698
     * When passed two or more arguments, a new module is created.  If passed only one argument, an
1699
     * existing module (the name passed as the first argument to `module`) is retrieved.
1700
     *
1701
     *
1702
     * # Module
1703
     *
1704
     * A module is a collection of services, directives, controllers, filters, and configuration information.
1705
     * `angular.module` is used to configure the {@link auto.$injector $injector}.
1706
     *
1707
     * ```js
1708
     * // Create a new module
1709
     * var myModule = angular.module('myModule', []);
1710
     *
1711
     * // register a new service
1712
     * myModule.value('appName', 'MyCoolApp');
1713
     *
1714
     * // configure existing services inside initialization blocks.
1715
     * myModule.config(['$locationProvider', function($locationProvider) {
1716
     *   // Configure existing providers
1717
     *   $locationProvider.hashPrefix('!');
1718
     * }]);
1719
     * ```
1720
     *
1721
     * Then you can create an injector and load your modules like this:
1722
     *
1723
     * ```js
1724
     * var injector = angular.injector(['ng', 'myModule'])
1725
     * ```
1726
     *
1727
     * However it's more likely that you'll just use
1728
     * {@link ng.directive:ngApp ngApp} or
1729
     * {@link angular.bootstrap} to simplify this process for you.
1730
     *
1731
     * @param {!string} name The name of the module to create or retrieve.
1732
     * @param {!Array.<string>=} requires If specified then new module is being created. If
1733
     *        unspecified then the module is being retrieved for further configuration.
1734
     * @param {Function=} configFn Optional configuration function for the module. Same as
1735
     *        {@link angular.Module#config Module#config()}.
1736
     * @returns {module} new module with the {@link angular.Module} api.
1737
     */
1738
    return function module(name, requires, configFn) {
1739
      var assertNotHasOwnProperty = function(name, context) {
1740
        if (name === 'hasOwnProperty') {
1741
          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1742
        }
1743
      };
1744
 
1745
      assertNotHasOwnProperty(name, 'module');
1746
      if (requires && modules.hasOwnProperty(name)) {
1747
        modules[name] = null;
1748
      }
1749
      return ensure(modules, name, function() {
1750
        if (!requires) {
1751
          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1752
             "the module name or forgot to load it. If registering a module ensure that you " +
1753
             "specify the dependencies as the second argument.", name);
1754
        }
1755
 
1756
        /** @type {!Array.<Array.<*>>} */
1757
        var invokeQueue = [];
1758
 
1759
        /** @type {!Array.<Function>} */
1760
        var configBlocks = [];
1761
 
1762
        /** @type {!Array.<Function>} */
1763
        var runBlocks = [];
1764
 
1765
        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1766
 
1767
        /** @type {angular.Module} */
1768
        var moduleInstance = {
1769
          // Private state
1770
          _invokeQueue: invokeQueue,
1771
          _configBlocks: configBlocks,
1772
          _runBlocks: runBlocks,
1773
 
1774
          /**
1775
           * @ngdoc property
1776
           * @name angular.Module#requires
1777
           * @module ng
1778
           *
1779
           * @description
1780
           * Holds the list of modules which the injector will load before the current module is
1781
           * loaded.
1782
           */
1783
          requires: requires,
1784
 
1785
          /**
1786
           * @ngdoc property
1787
           * @name angular.Module#name
1788
           * @module ng
1789
           *
1790
           * @description
1791
           * Name of the module.
1792
           */
1793
          name: name,
1794
 
1795
 
1796
          /**
1797
           * @ngdoc method
1798
           * @name angular.Module#provider
1799
           * @module ng
1800
           * @param {string} name service name
1801
           * @param {Function} providerType Construction function for creating new instance of the
1802
           *                                service.
1803
           * @description
1804
           * See {@link auto.$provide#provider $provide.provider()}.
1805
           */
1806
          provider: invokeLater('$provide', 'provider'),
1807
 
1808
          /**
1809
           * @ngdoc method
1810
           * @name angular.Module#factory
1811
           * @module ng
1812
           * @param {string} name service name
1813
           * @param {Function} providerFunction Function for creating new instance of the service.
1814
           * @description
1815
           * See {@link auto.$provide#factory $provide.factory()}.
1816
           */
1817
          factory: invokeLater('$provide', 'factory'),
1818
 
1819
          /**
1820
           * @ngdoc method
1821
           * @name angular.Module#service
1822
           * @module ng
1823
           * @param {string} name service name
1824
           * @param {Function} constructor A constructor function that will be instantiated.
1825
           * @description
1826
           * See {@link auto.$provide#service $provide.service()}.
1827
           */
1828
          service: invokeLater('$provide', 'service'),
1829
 
1830
          /**
1831
           * @ngdoc method
1832
           * @name angular.Module#value
1833
           * @module ng
1834
           * @param {string} name service name
1835
           * @param {*} object Service instance object.
1836
           * @description
1837
           * See {@link auto.$provide#value $provide.value()}.
1838
           */
1839
          value: invokeLater('$provide', 'value'),
1840
 
1841
          /**
1842
           * @ngdoc method
1843
           * @name angular.Module#constant
1844
           * @module ng
1845
           * @param {string} name constant name
1846
           * @param {*} object Constant value.
1847
           * @description
1848
           * Because the constant are fixed, they get applied before other provide methods.
1849
           * See {@link auto.$provide#constant $provide.constant()}.
1850
           */
1851
          constant: invokeLater('$provide', 'constant', 'unshift'),
1852
 
1853
          /**
1854
           * @ngdoc method
1855
           * @name angular.Module#animation
1856
           * @module ng
1857
           * @param {string} name animation name
1858
           * @param {Function} animationFactory Factory function for creating new instance of an
1859
           *                                    animation.
1860
           * @description
1861
           *
1862
           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1863
           *
1864
           *
1865
           * Defines an animation hook that can be later used with
1866
           * {@link ngAnimate.$animate $animate} service and directives that use this service.
1867
           *
1868
           * ```js
1869
           * module.animation('.animation-name', function($inject1, $inject2) {
1870
           *   return {
1871
           *     eventName : function(element, done) {
1872
           *       //code to run the animation
1873
           *       //once complete, then run done()
1874
           *       return function cancellationFunction(element) {
1875
           *         //code to cancel the animation
1876
           *       }
1877
           *     }
1878
           *   }
1879
           * })
1880
           * ```
1881
           *
1882
           * See {@link ng.$animateProvider#register $animateProvider.register()} and
1883
           * {@link ngAnimate ngAnimate module} for more information.
1884
           */
1885
          animation: invokeLater('$animateProvider', 'register'),
1886
 
1887
          /**
1888
           * @ngdoc method
1889
           * @name angular.Module#filter
1890
           * @module ng
1891
           * @param {string} name Filter name.
1892
           * @param {Function} filterFactory Factory function for creating new instance of filter.
1893
           * @description
1894
           * See {@link ng.$filterProvider#register $filterProvider.register()}.
1895
           */
1896
          filter: invokeLater('$filterProvider', 'register'),
1897
 
1898
          /**
1899
           * @ngdoc method
1900
           * @name angular.Module#controller
1901
           * @module ng
1902
           * @param {string|Object} name Controller name, or an object map of controllers where the
1903
           *    keys are the names and the values are the constructors.
1904
           * @param {Function} constructor Controller constructor function.
1905
           * @description
1906
           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1907
           */
1908
          controller: invokeLater('$controllerProvider', 'register'),
1909
 
1910
          /**
1911
           * @ngdoc method
1912
           * @name angular.Module#directive
1913
           * @module ng
1914
           * @param {string|Object} name Directive name, or an object map of directives where the
1915
           *    keys are the names and the values are the factories.
1916
           * @param {Function} directiveFactory Factory function for creating new instance of
1917
           * directives.
1918
           * @description
1919
           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1920
           */
1921
          directive: invokeLater('$compileProvider', 'directive'),
1922
 
1923
          /**
1924
           * @ngdoc method
1925
           * @name angular.Module#config
1926
           * @module ng
1927
           * @param {Function} configFn Execute this function on module load. Useful for service
1928
           *    configuration.
1929
           * @description
1930
           * Use this method to register work which needs to be performed on module loading.
1931
           * For more about how to configure services, see
1932
           * {@link providers#provider-recipe Provider Recipe}.
1933
           */
1934
          config: config,
1935
 
1936
          /**
1937
           * @ngdoc method
1938
           * @name angular.Module#run
1939
           * @module ng
1940
           * @param {Function} initializationFn Execute this function after injector creation.
1941
           *    Useful for application initialization.
1942
           * @description
1943
           * Use this method to register work which should be performed when the injector is done
1944
           * loading all modules.
1945
           */
1946
          run: function(block) {
1947
            runBlocks.push(block);
1948
            return this;
1949
          }
1950
        };
1951
 
1952
        if (configFn) {
1953
          config(configFn);
1954
        }
1955
 
1956
        return moduleInstance;
1957
 
1958
        /**
1959
         * @param {string} provider
1960
         * @param {string} method
1961
         * @param {String=} insertMethod
1962
         * @returns {angular.Module}
1963
         */
1964
        function invokeLater(provider, method, insertMethod, queue) {
1965
          if (!queue) queue = invokeQueue;
1966
          return function() {
1967
            queue[insertMethod || 'push']([provider, method, arguments]);
1968
            return moduleInstance;
1969
          };
1970
        }
1971
      });
1972
    };
1973
  });
1974
 
1975
}
1976
 
1977
/* global: toDebugString: true */
1978
 
1979
function serializeObject(obj) {
1980
  var seen = [];
1981
 
1982
  return JSON.stringify(obj, function(key, val) {
1983
    val = toJsonReplacer(key, val);
1984
    if (isObject(val)) {
1985
 
1986
      if (seen.indexOf(val) >= 0) return '<<already seen>>';
1987
 
1988
      seen.push(val);
1989
    }
1990
    return val;
1991
  });
1992
}
1993
 
1994
function toDebugString(obj) {
1995
  if (typeof obj === 'function') {
1996
    return obj.toString().replace(/ \{[\s\S]*$/, '');
1997
  } else if (typeof obj === 'undefined') {
1998
    return 'undefined';
1999
  } else if (typeof obj !== 'string') {
2000
    return serializeObject(obj);
2001
  }
2002
  return obj;
2003
}
2004
 
2005
/* global angularModule: true,
2006
  version: true,
2007
 
2008
  $LocaleProvider,
2009
  $CompileProvider,
2010
 
2011
  htmlAnchorDirective,
2012
  inputDirective,
2013
  inputDirective,
2014
  formDirective,
2015
  scriptDirective,
2016
  selectDirective,
2017
  styleDirective,
2018
  optionDirective,
2019
  ngBindDirective,
2020
  ngBindHtmlDirective,
2021
  ngBindTemplateDirective,
2022
  ngClassDirective,
2023
  ngClassEvenDirective,
2024
  ngClassOddDirective,
2025
  ngCspDirective,
2026
  ngCloakDirective,
2027
  ngControllerDirective,
2028
  ngFormDirective,
2029
  ngHideDirective,
2030
  ngIfDirective,
2031
  ngIncludeDirective,
2032
  ngIncludeFillContentDirective,
2033
  ngInitDirective,
2034
  ngNonBindableDirective,
2035
  ngPluralizeDirective,
2036
  ngRepeatDirective,
2037
  ngShowDirective,
2038
  ngStyleDirective,
2039
  ngSwitchDirective,
2040
  ngSwitchWhenDirective,
2041
  ngSwitchDefaultDirective,
2042
  ngOptionsDirective,
2043
  ngTranscludeDirective,
2044
  ngModelDirective,
2045
  ngListDirective,
2046
  ngChangeDirective,
2047
  patternDirective,
2048
  patternDirective,
2049
  requiredDirective,
2050
  requiredDirective,
2051
  minlengthDirective,
2052
  minlengthDirective,
2053
  maxlengthDirective,
2054
  maxlengthDirective,
2055
  ngValueDirective,
2056
  ngModelOptionsDirective,
2057
  ngAttributeAliasDirectives,
2058
  ngEventDirectives,
2059
 
2060
  $AnchorScrollProvider,
2061
  $AnimateProvider,
2062
  $BrowserProvider,
2063
  $CacheFactoryProvider,
2064
  $ControllerProvider,
2065
  $DocumentProvider,
2066
  $ExceptionHandlerProvider,
2067
  $FilterProvider,
2068
  $InterpolateProvider,
2069
  $IntervalProvider,
2070
  $HttpProvider,
2071
  $HttpBackendProvider,
2072
  $LocationProvider,
2073
  $LogProvider,
2074
  $ParseProvider,
2075
  $RootScopeProvider,
2076
  $QProvider,
2077
  $$QProvider,
2078
  $$SanitizeUriProvider,
2079
  $SceProvider,
2080
  $SceDelegateProvider,
2081
  $SnifferProvider,
2082
  $TemplateCacheProvider,
2083
  $TemplateRequestProvider,
2084
  $$TestabilityProvider,
2085
  $TimeoutProvider,
2086
  $$RAFProvider,
2087
  $$AsyncCallbackProvider,
2088
  $WindowProvider,
2089
  $$jqLiteProvider
2090
*/
2091
 
2092
 
2093
/**
2094
 * @ngdoc object
2095
 * @name angular.version
2096
 * @module ng
2097
 * @description
2098
 * An object that contains information about the current AngularJS version. This object has the
2099
 * following properties:
2100
 *
2101
 * - `full` – `{string}` – Full version string, such as "0.9.18".
2102
 * - `major` – `{number}` – Major version number, such as "0".
2103
 * - `minor` – `{number}` – Minor version number, such as "9".
2104
 * - `dot` – `{number}` – Dot version number, such as "18".
2105
 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2106
 */
2107
var version = {
2108
  full: '1.3.6',    // all of these placeholder strings will be replaced by grunt's
2109
  major: 1,    // package task
2110
  minor: 3,
2111
  dot: 6,
2112
  codeName: 'robofunky-danceblaster'
2113
};
2114
 
2115
 
2116
function publishExternalAPI(angular) {
2117
  extend(angular, {
2118
    'bootstrap': bootstrap,
2119
    'copy': copy,
2120
    'extend': extend,
2121
    'equals': equals,
2122
    'element': jqLite,
2123
    'forEach': forEach,
2124
    'injector': createInjector,
2125
    'noop': noop,
2126
    'bind': bind,
2127
    'toJson': toJson,
2128
    'fromJson': fromJson,
2129
    'identity': identity,
2130
    'isUndefined': isUndefined,
2131
    'isDefined': isDefined,
2132
    'isString': isString,
2133
    'isFunction': isFunction,
2134
    'isObject': isObject,
2135
    'isNumber': isNumber,
2136
    'isElement': isElement,
2137
    'isArray': isArray,
2138
    'version': version,
2139
    'isDate': isDate,
2140
    'lowercase': lowercase,
2141
    'uppercase': uppercase,
2142
    'callbacks': {counter: 0},
2143
    'getTestability': getTestability,
2144
    '$$minErr': minErr,
2145
    '$$csp': csp,
2146
    'reloadWithDebugInfo': reloadWithDebugInfo
2147
  });
2148
 
2149
  angularModule = setupModuleLoader(window);
2150
  try {
2151
    angularModule('ngLocale');
2152
  } catch (e) {
2153
    angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2154
  }
2155
 
2156
  angularModule('ng', ['ngLocale'], ['$provide',
2157
    function ngModule($provide) {
2158
      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2159
      $provide.provider({
2160
        $$sanitizeUri: $$SanitizeUriProvider
2161
      });
2162
      $provide.provider('$compile', $CompileProvider).
2163
        directive({
2164
            a: htmlAnchorDirective,
2165
            input: inputDirective,
2166
            textarea: inputDirective,
2167
            form: formDirective,
2168
            script: scriptDirective,
2169
            select: selectDirective,
2170
            style: styleDirective,
2171
            option: optionDirective,
2172
            ngBind: ngBindDirective,
2173
            ngBindHtml: ngBindHtmlDirective,
2174
            ngBindTemplate: ngBindTemplateDirective,
2175
            ngClass: ngClassDirective,
2176
            ngClassEven: ngClassEvenDirective,
2177
            ngClassOdd: ngClassOddDirective,
2178
            ngCloak: ngCloakDirective,
2179
            ngController: ngControllerDirective,
2180
            ngForm: ngFormDirective,
2181
            ngHide: ngHideDirective,
2182
            ngIf: ngIfDirective,
2183
            ngInclude: ngIncludeDirective,
2184
            ngInit: ngInitDirective,
2185
            ngNonBindable: ngNonBindableDirective,
2186
            ngPluralize: ngPluralizeDirective,
2187
            ngRepeat: ngRepeatDirective,
2188
            ngShow: ngShowDirective,
2189
            ngStyle: ngStyleDirective,
2190
            ngSwitch: ngSwitchDirective,
2191
            ngSwitchWhen: ngSwitchWhenDirective,
2192
            ngSwitchDefault: ngSwitchDefaultDirective,
2193
            ngOptions: ngOptionsDirective,
2194
            ngTransclude: ngTranscludeDirective,
2195
            ngModel: ngModelDirective,
2196
            ngList: ngListDirective,
2197
            ngChange: ngChangeDirective,
2198
            pattern: patternDirective,
2199
            ngPattern: patternDirective,
2200
            required: requiredDirective,
2201
            ngRequired: requiredDirective,
2202
            minlength: minlengthDirective,
2203
            ngMinlength: minlengthDirective,
2204
            maxlength: maxlengthDirective,
2205
            ngMaxlength: maxlengthDirective,
2206
            ngValue: ngValueDirective,
2207
            ngModelOptions: ngModelOptionsDirective
2208
        }).
2209
        directive({
2210
          ngInclude: ngIncludeFillContentDirective
2211
        }).
2212
        directive(ngAttributeAliasDirectives).
2213
        directive(ngEventDirectives);
2214
      $provide.provider({
2215
        $anchorScroll: $AnchorScrollProvider,
2216
        $animate: $AnimateProvider,
2217
        $browser: $BrowserProvider,
2218
        $cacheFactory: $CacheFactoryProvider,
2219
        $controller: $ControllerProvider,
2220
        $document: $DocumentProvider,
2221
        $exceptionHandler: $ExceptionHandlerProvider,
2222
        $filter: $FilterProvider,
2223
        $interpolate: $InterpolateProvider,
2224
        $interval: $IntervalProvider,
2225
        $http: $HttpProvider,
2226
        $httpBackend: $HttpBackendProvider,
2227
        $location: $LocationProvider,
2228
        $log: $LogProvider,
2229
        $parse: $ParseProvider,
2230
        $rootScope: $RootScopeProvider,
2231
        $q: $QProvider,
2232
        $$q: $$QProvider,
2233
        $sce: $SceProvider,
2234
        $sceDelegate: $SceDelegateProvider,
2235
        $sniffer: $SnifferProvider,
2236
        $templateCache: $TemplateCacheProvider,
2237
        $templateRequest: $TemplateRequestProvider,
2238
        $$testability: $$TestabilityProvider,
2239
        $timeout: $TimeoutProvider,
2240
        $window: $WindowProvider,
2241
        $$rAF: $$RAFProvider,
2242
        $$asyncCallback: $$AsyncCallbackProvider,
2243
        $$jqLite: $$jqLiteProvider
2244
      });
2245
    }
2246
  ]);
2247
}
2248
 
2249
/* global JQLitePrototype: true,
2250
  addEventListenerFn: true,
2251
  removeEventListenerFn: true,
2252
  BOOLEAN_ATTR: true,
2253
  ALIASED_ATTR: true,
2254
*/
2255
 
2256
//////////////////////////////////
2257
//JQLite
2258
//////////////////////////////////
2259
 
2260
/**
2261
 * @ngdoc function
2262
 * @name angular.element
2263
 * @module ng
2264
 * @kind function
2265
 *
2266
 * @description
2267
 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2268
 *
2269
 * If jQuery is available, `angular.element` is an alias for the
2270
 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2271
 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2272
 *
2273
 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2274
 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2275
 * commonly needed functionality with the goal of having a very small footprint.</div>
2276
 *
2277
 * To use jQuery, simply load it before `DOMContentLoaded` event fired.
2278
 *
2279
 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2280
 * jqLite; they are never raw DOM references.</div>
2281
 *
2282
 * ## Angular's jqLite
2283
 * jqLite provides only the following jQuery methods:
2284
 *
2285
 * - [`addClass()`](http://api.jquery.com/addClass/)
2286
 * - [`after()`](http://api.jquery.com/after/)
2287
 * - [`append()`](http://api.jquery.com/append/)
2288
 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2289
 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2290
 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2291
 * - [`clone()`](http://api.jquery.com/clone/)
2292
 * - [`contents()`](http://api.jquery.com/contents/)
2293
 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`
2294
 * - [`data()`](http://api.jquery.com/data/)
2295
 * - [`detach()`](http://api.jquery.com/detach/)
2296
 * - [`empty()`](http://api.jquery.com/empty/)
2297
 * - [`eq()`](http://api.jquery.com/eq/)
2298
 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2299
 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2300
 * - [`html()`](http://api.jquery.com/html/)
2301
 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2302
 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2303
 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2304
 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2305
 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2306
 * - [`prepend()`](http://api.jquery.com/prepend/)
2307
 * - [`prop()`](http://api.jquery.com/prop/)
2308
 * - [`ready()`](http://api.jquery.com/ready/)
2309
 * - [`remove()`](http://api.jquery.com/remove/)
2310
 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2311
 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2312
 * - [`removeData()`](http://api.jquery.com/removeData/)
2313
 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2314
 * - [`text()`](http://api.jquery.com/text/)
2315
 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2316
 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2317
 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2318
 * - [`val()`](http://api.jquery.com/val/)
2319
 * - [`wrap()`](http://api.jquery.com/wrap/)
2320
 *
2321
 * ## jQuery/jqLite Extras
2322
 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2323
 *
2324
 * ### Events
2325
 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2326
 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2327
 *    element before it is removed.
2328
 *
2329
 * ### Methods
2330
 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2331
 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2332
 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2333
 *   `'ngModel'`).
2334
 * - `injector()` - retrieves the injector of the current element or its parent.
2335
 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2336
 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2337
 *   be enabled.
2338
 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2339
 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2340
 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2341
 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2342
 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2343
 *   parent element is reached.
2344
 *
2345
 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2346
 * @returns {Object} jQuery object.
2347
 */
2348
 
2349
JQLite.expando = 'ng339';
2350
 
2351
var jqCache = JQLite.cache = {},
2352
    jqId = 1,
2353
    addEventListenerFn = function(element, type, fn) {
2354
      element.addEventListener(type, fn, false);
2355
    },
2356
    removeEventListenerFn = function(element, type, fn) {
2357
      element.removeEventListener(type, fn, false);
2358
    };
2359
 
2360
/*
2361
 * !!! This is an undocumented "private" function !!!
2362
 */
2363
JQLite._data = function(node) {
2364
  //jQuery always returns an object on cache miss
2365
  return this.cache[node[this.expando]] || {};
2366
};
2367
 
2368
function jqNextId() { return ++jqId; }
2369
 
2370
 
2371
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2372
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2373
var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2374
var jqLiteMinErr = minErr('jqLite');
2375
 
2376
/**
2377
 * Converts snake_case to camelCase.
2378
 * Also there is special case for Moz prefix starting with upper case letter.
2379
 * @param name Name to normalize
2380
 */
2381
function camelCase(name) {
2382
  return name.
2383
    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2384
      return offset ? letter.toUpperCase() : letter;
2385
    }).
2386
    replace(MOZ_HACK_REGEXP, 'Moz$1');
2387
}
2388
 
2389
var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2390
var HTML_REGEXP = /<|&#?\w+;/;
2391
var TAG_NAME_REGEXP = /<([\w:]+)/;
2392
var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2393
 
2394
var wrapMap = {
2395
  'option': [1, '<select multiple="multiple">', '</select>'],
2396
 
2397
  'thead': [1, '<table>', '</table>'],
2398
  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2399
  'tr': [2, '<table><tbody>', '</tbody></table>'],
2400
  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2401
  '_default': [0, "", ""]
2402
};
2403
 
2404
wrapMap.optgroup = wrapMap.option;
2405
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2406
wrapMap.th = wrapMap.td;
2407
 
2408
 
2409
function jqLiteIsTextNode(html) {
2410
  return !HTML_REGEXP.test(html);
2411
}
2412
 
2413
function jqLiteAcceptsData(node) {
2414
  // The window object can accept data but has no nodeType
2415
  // Otherwise we are only interested in elements (1) and documents (9)
2416
  var nodeType = node.nodeType;
2417
  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2418
}
2419
 
2420
function jqLiteBuildFragment(html, context) {
2421
  var tmp, tag, wrap,
2422
      fragment = context.createDocumentFragment(),
2423
      nodes = [], i;
2424
 
2425
  if (jqLiteIsTextNode(html)) {
2426
    // Convert non-html into a text node
2427
    nodes.push(context.createTextNode(html));
2428
  } else {
2429
    // Convert html into DOM nodes
2430
    tmp = tmp || fragment.appendChild(context.createElement("div"));
2431
    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2432
    wrap = wrapMap[tag] || wrapMap._default;
2433
    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2434
 
2435
    // Descend through wrappers to the right content
2436
    i = wrap[0];
2437
    while (i--) {
2438
      tmp = tmp.lastChild;
2439
    }
2440
 
2441
    nodes = concat(nodes, tmp.childNodes);
2442
 
2443
    tmp = fragment.firstChild;
2444
    tmp.textContent = "";
2445
  }
2446
 
2447
  // Remove wrapper from fragment
2448
  fragment.textContent = "";
2449
  fragment.innerHTML = ""; // Clear inner HTML
2450
  forEach(nodes, function(node) {
2451
    fragment.appendChild(node);
2452
  });
2453
 
2454
  return fragment;
2455
}
2456
 
2457
function jqLiteParseHTML(html, context) {
2458
  context = context || document;
2459
  var parsed;
2460
 
2461
  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2462
    return [context.createElement(parsed[1])];
2463
  }
2464
 
2465
  if ((parsed = jqLiteBuildFragment(html, context))) {
2466
    return parsed.childNodes;
2467
  }
2468
 
2469
  return [];
2470
}
2471
 
2472
/////////////////////////////////////////////
2473
function JQLite(element) {
2474
  if (element instanceof JQLite) {
2475
    return element;
2476
  }
2477
 
2478
  var argIsString;
2479
 
2480
  if (isString(element)) {
2481
    element = trim(element);
2482
    argIsString = true;
2483
  }
2484
  if (!(this instanceof JQLite)) {
2485
    if (argIsString && element.charAt(0) != '<') {
2486
      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2487
    }
2488
    return new JQLite(element);
2489
  }
2490
 
2491
  if (argIsString) {
2492
    jqLiteAddNodes(this, jqLiteParseHTML(element));
2493
  } else {
2494
    jqLiteAddNodes(this, element);
2495
  }
2496
}
2497
 
2498
function jqLiteClone(element) {
2499
  return element.cloneNode(true);
2500
}
2501
 
2502
function jqLiteDealoc(element, onlyDescendants) {
2503
  if (!onlyDescendants) jqLiteRemoveData(element);
2504
 
2505
  if (element.querySelectorAll) {
2506
    var descendants = element.querySelectorAll('*');
2507
    for (var i = 0, l = descendants.length; i < l; i++) {
2508
      jqLiteRemoveData(descendants[i]);
2509
    }
2510
  }
2511
}
2512
 
2513
function jqLiteOff(element, type, fn, unsupported) {
2514
  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2515
 
2516
  var expandoStore = jqLiteExpandoStore(element);
2517
  var events = expandoStore && expandoStore.events;
2518
  var handle = expandoStore && expandoStore.handle;
2519
 
2520
  if (!handle) return; //no listeners registered
2521
 
2522
  if (!type) {
2523
    for (type in events) {
2524
      if (type !== '$destroy') {
2525
        removeEventListenerFn(element, type, handle);
2526
      }
2527
      delete events[type];
2528
    }
2529
  } else {
2530
    forEach(type.split(' '), function(type) {
2531
      if (isDefined(fn)) {
2532
        var listenerFns = events[type];
2533
        arrayRemove(listenerFns || [], fn);
2534
        if (listenerFns && listenerFns.length > 0) {
2535
          return;
2536
        }
2537
      }
2538
 
2539
      removeEventListenerFn(element, type, handle);
2540
      delete events[type];
2541
    });
2542
  }
2543
}
2544
 
2545
function jqLiteRemoveData(element, name) {
2546
  var expandoId = element.ng339;
2547
  var expandoStore = expandoId && jqCache[expandoId];
2548
 
2549
  if (expandoStore) {
2550
    if (name) {
2551
      delete expandoStore.data[name];
2552
      return;
2553
    }
2554
 
2555
    if (expandoStore.handle) {
2556
      if (expandoStore.events.$destroy) {
2557
        expandoStore.handle({}, '$destroy');
2558
      }
2559
      jqLiteOff(element);
2560
    }
2561
    delete jqCache[expandoId];
2562
    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2563
  }
2564
}
2565
 
2566
 
2567
function jqLiteExpandoStore(element, createIfNecessary) {
2568
  var expandoId = element.ng339,
2569
      expandoStore = expandoId && jqCache[expandoId];
2570
 
2571
  if (createIfNecessary && !expandoStore) {
2572
    element.ng339 = expandoId = jqNextId();
2573
    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2574
  }
2575
 
2576
  return expandoStore;
2577
}
2578
 
2579
 
2580
function jqLiteData(element, key, value) {
2581
  if (jqLiteAcceptsData(element)) {
2582
 
2583
    var isSimpleSetter = isDefined(value);
2584
    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2585
    var massGetter = !key;
2586
    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2587
    var data = expandoStore && expandoStore.data;
2588
 
2589
    if (isSimpleSetter) { // data('key', value)
2590
      data[key] = value;
2591
    } else {
2592
      if (massGetter) {  // data()
2593
        return data;
2594
      } else {
2595
        if (isSimpleGetter) { // data('key')
2596
          // don't force creation of expandoStore if it doesn't exist yet
2597
          return data && data[key];
2598
        } else { // mass-setter: data({key1: val1, key2: val2})
2599
          extend(data, key);
2600
        }
2601
      }
2602
    }
2603
  }
2604
}
2605
 
2606
function jqLiteHasClass(element, selector) {
2607
  if (!element.getAttribute) return false;
2608
  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2609
      indexOf(" " + selector + " ") > -1);
2610
}
2611
 
2612
function jqLiteRemoveClass(element, cssClasses) {
2613
  if (cssClasses && element.setAttribute) {
2614
    forEach(cssClasses.split(' '), function(cssClass) {
2615
      element.setAttribute('class', trim(
2616
          (" " + (element.getAttribute('class') || '') + " ")
2617
          .replace(/[\n\t]/g, " ")
2618
          .replace(" " + trim(cssClass) + " ", " "))
2619
      );
2620
    });
2621
  }
2622
}
2623
 
2624
function jqLiteAddClass(element, cssClasses) {
2625
  if (cssClasses && element.setAttribute) {
2626
    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2627
                            .replace(/[\n\t]/g, " ");
2628
 
2629
    forEach(cssClasses.split(' '), function(cssClass) {
2630
      cssClass = trim(cssClass);
2631
      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2632
        existingClasses += cssClass + ' ';
2633
      }
2634
    });
2635
 
2636
    element.setAttribute('class', trim(existingClasses));
2637
  }
2638
}
2639
 
2640
 
2641
function jqLiteAddNodes(root, elements) {
2642
  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2643
 
2644
  if (elements) {
2645
 
2646
    // if a Node (the most common case)
2647
    if (elements.nodeType) {
2648
      root[root.length++] = elements;
2649
    } else {
2650
      var length = elements.length;
2651
 
2652
      // if an Array or NodeList and not a Window
2653
      if (typeof length === 'number' && elements.window !== elements) {
2654
        if (length) {
2655
          for (var i = 0; i < length; i++) {
2656
            root[root.length++] = elements[i];
2657
          }
2658
        }
2659
      } else {
2660
        root[root.length++] = elements;
2661
      }
2662
    }
2663
  }
2664
}
2665
 
2666
 
2667
function jqLiteController(element, name) {
2668
  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2669
}
2670
 
2671
function jqLiteInheritedData(element, name, value) {
2672
  // if element is the document object work with the html element instead
2673
  // this makes $(document).scope() possible
2674
  if (element.nodeType == NODE_TYPE_DOCUMENT) {
2675
    element = element.documentElement;
2676
  }
2677
  var names = isArray(name) ? name : [name];
2678
 
2679
  while (element) {
2680
    for (var i = 0, ii = names.length; i < ii; i++) {
2681
      if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2682
    }
2683
 
2684
    // If dealing with a document fragment node with a host element, and no parent, use the host
2685
    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2686
    // to lookup parent controllers.
2687
    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2688
  }
2689
}
2690
 
2691
function jqLiteEmpty(element) {
2692
  jqLiteDealoc(element, true);
2693
  while (element.firstChild) {
2694
    element.removeChild(element.firstChild);
2695
  }
2696
}
2697
 
2698
function jqLiteRemove(element, keepData) {
2699
  if (!keepData) jqLiteDealoc(element);
2700
  var parent = element.parentNode;
2701
  if (parent) parent.removeChild(element);
2702
}
2703
 
2704
 
2705
function jqLiteDocumentLoaded(action, win) {
2706
  win = win || window;
2707
  if (win.document.readyState === 'complete') {
2708
    // Force the action to be run async for consistent behaviour
2709
    // from the action's point of view
2710
    // i.e. it will definitely not be in a $apply
2711
    win.setTimeout(action);
2712
  } else {
2713
    // No need to unbind this handler as load is only ever called once
2714
    jqLite(win).on('load', action);
2715
  }
2716
}
2717
 
2718
//////////////////////////////////////////
2719
// Functions which are declared directly.
2720
//////////////////////////////////////////
2721
var JQLitePrototype = JQLite.prototype = {
2722
  ready: function(fn) {
2723
    var fired = false;
2724
 
2725
    function trigger() {
2726
      if (fired) return;
2727
      fired = true;
2728
      fn();
2729
    }
2730
 
2731
    // check if document is already loaded
2732
    if (document.readyState === 'complete') {
2733
      setTimeout(trigger);
2734
    } else {
2735
      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2736
      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2737
      // jshint -W064
2738
      JQLite(window).on('load', trigger); // fallback to window.onload for others
2739
      // jshint +W064
2740
    }
2741
  },
2742
  toString: function() {
2743
    var value = [];
2744
    forEach(this, function(e) { value.push('' + e);});
2745
    return '[' + value.join(', ') + ']';
2746
  },
2747
 
2748
  eq: function(index) {
2749
      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2750
  },
2751
 
2752
  length: 0,
2753
  push: push,
2754
  sort: [].sort,
2755
  splice: [].splice
2756
};
2757
 
2758
//////////////////////////////////////////
2759
// Functions iterating getter/setters.
2760
// these functions return self on setter and
2761
// value on get.
2762
//////////////////////////////////////////
2763
var BOOLEAN_ATTR = {};
2764
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2765
  BOOLEAN_ATTR[lowercase(value)] = value;
2766
});
2767
var BOOLEAN_ELEMENTS = {};
2768
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2769
  BOOLEAN_ELEMENTS[value] = true;
2770
});
2771
var ALIASED_ATTR = {
2772
  'ngMinlength': 'minlength',
2773
  'ngMaxlength': 'maxlength',
2774
  'ngMin': 'min',
2775
  'ngMax': 'max',
2776
  'ngPattern': 'pattern'
2777
};
2778
 
2779
function getBooleanAttrName(element, name) {
2780
  // check dom last since we will most likely fail on name
2781
  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2782
 
2783
  // booleanAttr is here twice to minimize DOM access
2784
  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
2785
}
2786
 
2787
function getAliasedAttrName(element, name) {
2788
  var nodeName = element.nodeName;
2789
  return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2790
}
2791
 
2792
forEach({
2793
  data: jqLiteData,
2794
  removeData: jqLiteRemoveData
2795
}, function(fn, name) {
2796
  JQLite[name] = fn;
2797
});
2798
 
2799
forEach({
2800
  data: jqLiteData,
2801
  inheritedData: jqLiteInheritedData,
2802
 
2803
  scope: function(element) {
2804
    // Can't use jqLiteData here directly so we stay compatible with jQuery!
2805
    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2806
  },
2807
 
2808
  isolateScope: function(element) {
2809
    // Can't use jqLiteData here directly so we stay compatible with jQuery!
2810
    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2811
  },
2812
 
2813
  controller: jqLiteController,
2814
 
2815
  injector: function(element) {
2816
    return jqLiteInheritedData(element, '$injector');
2817
  },
2818
 
2819
  removeAttr: function(element, name) {
2820
    element.removeAttribute(name);
2821
  },
2822
 
2823
  hasClass: jqLiteHasClass,
2824
 
2825
  css: function(element, name, value) {
2826
    name = camelCase(name);
2827
 
2828
    if (isDefined(value)) {
2829
      element.style[name] = value;
2830
    } else {
2831
      return element.style[name];
2832
    }
2833
  },
2834
 
2835
  attr: function(element, name, value) {
2836
    var lowercasedName = lowercase(name);
2837
    if (BOOLEAN_ATTR[lowercasedName]) {
2838
      if (isDefined(value)) {
2839
        if (!!value) {
2840
          element[name] = true;
2841
          element.setAttribute(name, lowercasedName);
2842
        } else {
2843
          element[name] = false;
2844
          element.removeAttribute(lowercasedName);
2845
        }
2846
      } else {
2847
        return (element[name] ||
2848
                 (element.attributes.getNamedItem(name) || noop).specified)
2849
               ? lowercasedName
2850
               : undefined;
2851
      }
2852
    } else if (isDefined(value)) {
2853
      element.setAttribute(name, value);
2854
    } else if (element.getAttribute) {
2855
      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2856
      // some elements (e.g. Document) don't have get attribute, so return undefined
2857
      var ret = element.getAttribute(name, 2);
2858
      // normalize non-existing attributes to undefined (as jQuery)
2859
      return ret === null ? undefined : ret;
2860
    }
2861
  },
2862
 
2863
  prop: function(element, name, value) {
2864
    if (isDefined(value)) {
2865
      element[name] = value;
2866
    } else {
2867
      return element[name];
2868
    }
2869
  },
2870
 
2871
  text: (function() {
2872
    getText.$dv = '';
2873
    return getText;
2874
 
2875
    function getText(element, value) {
2876
      if (isUndefined(value)) {
2877
        var nodeType = element.nodeType;
2878
        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
2879
      }
2880
      element.textContent = value;
2881
    }
2882
  })(),
2883
 
2884
  val: function(element, value) {
2885
    if (isUndefined(value)) {
2886
      if (element.multiple && nodeName_(element) === 'select') {
2887
        var result = [];
2888
        forEach(element.options, function(option) {
2889
          if (option.selected) {
2890
            result.push(option.value || option.text);
2891
          }
2892
        });
2893
        return result.length === 0 ? null : result;
2894
      }
2895
      return element.value;
2896
    }
2897
    element.value = value;
2898
  },
2899
 
2900
  html: function(element, value) {
2901
    if (isUndefined(value)) {
2902
      return element.innerHTML;
2903
    }
2904
    jqLiteDealoc(element, true);
2905
    element.innerHTML = value;
2906
  },
2907
 
2908
  empty: jqLiteEmpty
2909
}, function(fn, name) {
2910
  /**
2911
   * Properties: writes return selection, reads return first value
2912
   */
2913
  JQLite.prototype[name] = function(arg1, arg2) {
2914
    var i, key;
2915
    var nodeCount = this.length;
2916
 
2917
    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2918
    // in a way that survives minification.
2919
    // jqLiteEmpty takes no arguments but is a setter.
2920
    if (fn !== jqLiteEmpty &&
2921
        (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2922
      if (isObject(arg1)) {
2923
 
2924
        // we are a write, but the object properties are the key/values
2925
        for (i = 0; i < nodeCount; i++) {
2926
          if (fn === jqLiteData) {
2927
            // data() takes the whole object in jQuery
2928
            fn(this[i], arg1);
2929
          } else {
2930
            for (key in arg1) {
2931
              fn(this[i], key, arg1[key]);
2932
            }
2933
          }
2934
        }
2935
        // return self for chaining
2936
        return this;
2937
      } else {
2938
        // we are a read, so read the first child.
2939
        // TODO: do we still need this?
2940
        var value = fn.$dv;
2941
        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2942
        var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2943
        for (var j = 0; j < jj; j++) {
2944
          var nodeValue = fn(this[j], arg1, arg2);
2945
          value = value ? value + nodeValue : nodeValue;
2946
        }
2947
        return value;
2948
      }
2949
    } else {
2950
      // we are a write, so apply to all children
2951
      for (i = 0; i < nodeCount; i++) {
2952
        fn(this[i], arg1, arg2);
2953
      }
2954
      // return self for chaining
2955
      return this;
2956
    }
2957
  };
2958
});
2959
 
2960
function createEventHandler(element, events) {
2961
  var eventHandler = function(event, type) {
2962
    // jQuery specific api
2963
    event.isDefaultPrevented = function() {
2964
      return event.defaultPrevented;
2965
    };
2966
 
2967
    var eventFns = events[type || event.type];
2968
    var eventFnsLength = eventFns ? eventFns.length : 0;
2969
 
2970
    if (!eventFnsLength) return;
2971
 
2972
    if (isUndefined(event.immediatePropagationStopped)) {
2973
      var originalStopImmediatePropagation = event.stopImmediatePropagation;
2974
      event.stopImmediatePropagation = function() {
2975
        event.immediatePropagationStopped = true;
2976
 
2977
        if (event.stopPropagation) {
2978
          event.stopPropagation();
2979
        }
2980
 
2981
        if (originalStopImmediatePropagation) {
2982
          originalStopImmediatePropagation.call(event);
2983
        }
2984
      };
2985
    }
2986
 
2987
    event.isImmediatePropagationStopped = function() {
2988
      return event.immediatePropagationStopped === true;
2989
    };
2990
 
2991
    // Copy event handlers in case event handlers array is modified during execution.
2992
    if ((eventFnsLength > 1)) {
2993
      eventFns = shallowCopy(eventFns);
2994
    }
2995
 
2996
    for (var i = 0; i < eventFnsLength; i++) {
2997
      if (!event.isImmediatePropagationStopped()) {
2998
        eventFns[i].call(element, event);
2999
      }
3000
    }
3001
  };
3002
 
3003
  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3004
  //       events on `element`
3005
  eventHandler.elem = element;
3006
  return eventHandler;
3007
}
3008
 
3009
//////////////////////////////////////////
3010
// Functions iterating traversal.
3011
// These functions chain results into a single
3012
// selector.
3013
//////////////////////////////////////////
3014
forEach({
3015
  removeData: jqLiteRemoveData,
3016
 
3017
  on: function jqLiteOn(element, type, fn, unsupported) {
3018
    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3019
 
3020
    // Do not add event handlers to non-elements because they will not be cleaned up.
3021
    if (!jqLiteAcceptsData(element)) {
3022
      return;
3023
    }
3024
 
3025
    var expandoStore = jqLiteExpandoStore(element, true);
3026
    var events = expandoStore.events;
3027
    var handle = expandoStore.handle;
3028
 
3029
    if (!handle) {
3030
      handle = expandoStore.handle = createEventHandler(element, events);
3031
    }
3032
 
3033
    // http://jsperf.com/string-indexof-vs-split
3034
    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3035
    var i = types.length;
3036
 
3037
    while (i--) {
3038
      type = types[i];
3039
      var eventFns = events[type];
3040
 
3041
      if (!eventFns) {
3042
        events[type] = [];
3043
 
3044
        if (type === 'mouseenter' || type === 'mouseleave') {
3045
          // Refer to jQuery's implementation of mouseenter & mouseleave
3046
          // Read about mouseenter and mouseleave:
3047
          // http://www.quirksmode.org/js/events_mouse.html#link8
3048
 
3049
          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3050
            var target = this, related = event.relatedTarget;
3051
            // For mousenter/leave call the handler if related is outside the target.
3052
            // NB: No relatedTarget if the mouse left/entered the browser window
3053
            if (!related || (related !== target && !target.contains(related))) {
3054
              handle(event, type);
3055
            }
3056
          });
3057
 
3058
        } else {
3059
          if (type !== '$destroy') {
3060
            addEventListenerFn(element, type, handle);
3061
          }
3062
        }
3063
        eventFns = events[type];
3064
      }
3065
      eventFns.push(fn);
3066
    }
3067
  },
3068
 
3069
  off: jqLiteOff,
3070
 
3071
  one: function(element, type, fn) {
3072
    element = jqLite(element);
3073
 
3074
    //add the listener twice so that when it is called
3075
    //you can remove the original function and still be
3076
    //able to call element.off(ev, fn) normally
3077
    element.on(type, function onFn() {
3078
      element.off(type, fn);
3079
      element.off(type, onFn);
3080
    });
3081
    element.on(type, fn);
3082
  },
3083
 
3084
  replaceWith: function(element, replaceNode) {
3085
    var index, parent = element.parentNode;
3086
    jqLiteDealoc(element);
3087
    forEach(new JQLite(replaceNode), function(node) {
3088
      if (index) {
3089
        parent.insertBefore(node, index.nextSibling);
3090
      } else {
3091
        parent.replaceChild(node, element);
3092
      }
3093
      index = node;
3094
    });
3095
  },
3096
 
3097
  children: function(element) {
3098
    var children = [];
3099
    forEach(element.childNodes, function(element) {
3100
      if (element.nodeType === NODE_TYPE_ELEMENT)
3101
        children.push(element);
3102
    });
3103
    return children;
3104
  },
3105
 
3106
  contents: function(element) {
3107
    return element.contentDocument || element.childNodes || [];
3108
  },
3109
 
3110
  append: function(element, node) {
3111
    var nodeType = element.nodeType;
3112
    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3113
 
3114
    node = new JQLite(node);
3115
 
3116
    for (var i = 0, ii = node.length; i < ii; i++) {
3117
      var child = node[i];
3118
      element.appendChild(child);
3119
    }
3120
  },
3121
 
3122
  prepend: function(element, node) {
3123
    if (element.nodeType === NODE_TYPE_ELEMENT) {
3124
      var index = element.firstChild;
3125
      forEach(new JQLite(node), function(child) {
3126
        element.insertBefore(child, index);
3127
      });
3128
    }
3129
  },
3130
 
3131
  wrap: function(element, wrapNode) {
3132
    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3133
    var parent = element.parentNode;
3134
    if (parent) {
3135
      parent.replaceChild(wrapNode, element);
3136
    }
3137
    wrapNode.appendChild(element);
3138
  },
3139
 
3140
  remove: jqLiteRemove,
3141
 
3142
  detach: function(element) {
3143
    jqLiteRemove(element, true);
3144
  },
3145
 
3146
  after: function(element, newElement) {
3147
    var index = element, parent = element.parentNode;
3148
    newElement = new JQLite(newElement);
3149
 
3150
    for (var i = 0, ii = newElement.length; i < ii; i++) {
3151
      var node = newElement[i];
3152
      parent.insertBefore(node, index.nextSibling);
3153
      index = node;
3154
    }
3155
  },
3156
 
3157
  addClass: jqLiteAddClass,
3158
  removeClass: jqLiteRemoveClass,
3159
 
3160
  toggleClass: function(element, selector, condition) {
3161
    if (selector) {
3162
      forEach(selector.split(' '), function(className) {
3163
        var classCondition = condition;
3164
        if (isUndefined(classCondition)) {
3165
          classCondition = !jqLiteHasClass(element, className);
3166
        }
3167
        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3168
      });
3169
    }
3170
  },
3171
 
3172
  parent: function(element) {
3173
    var parent = element.parentNode;
3174
    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3175
  },
3176
 
3177
  next: function(element) {
3178
    return element.nextElementSibling;
3179
  },
3180
 
3181
  find: function(element, selector) {
3182
    if (element.getElementsByTagName) {
3183
      return element.getElementsByTagName(selector);
3184
    } else {
3185
      return [];
3186
    }
3187
  },
3188
 
3189
  clone: jqLiteClone,
3190
 
3191
  triggerHandler: function(element, event, extraParameters) {
3192
 
3193
    var dummyEvent, eventFnsCopy, handlerArgs;
3194
    var eventName = event.type || event;
3195
    var expandoStore = jqLiteExpandoStore(element);
3196
    var events = expandoStore && expandoStore.events;
3197
    var eventFns = events && events[eventName];
3198
 
3199
    if (eventFns) {
3200
      // Create a dummy event to pass to the handlers
3201
      dummyEvent = {
3202
        preventDefault: function() { this.defaultPrevented = true; },
3203
        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3204
        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3205
        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3206
        stopPropagation: noop,
3207
        type: eventName,
3208
        target: element
3209
      };
3210
 
3211
      // If a custom event was provided then extend our dummy event with it
3212
      if (event.type) {
3213
        dummyEvent = extend(dummyEvent, event);
3214
      }
3215
 
3216
      // Copy event handlers in case event handlers array is modified during execution.
3217
      eventFnsCopy = shallowCopy(eventFns);
3218
      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3219
 
3220
      forEach(eventFnsCopy, function(fn) {
3221
        if (!dummyEvent.isImmediatePropagationStopped()) {
3222
          fn.apply(element, handlerArgs);
3223
        }
3224
      });
3225
    }
3226
  }
3227
}, function(fn, name) {
3228
  /**
3229
   * chaining functions
3230
   */
3231
  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3232
    var value;
3233
 
3234
    for (var i = 0, ii = this.length; i < ii; i++) {
3235
      if (isUndefined(value)) {
3236
        value = fn(this[i], arg1, arg2, arg3);
3237
        if (isDefined(value)) {
3238
          // any function which returns a value needs to be wrapped
3239
          value = jqLite(value);
3240
        }
3241
      } else {
3242
        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3243
      }
3244
    }
3245
    return isDefined(value) ? value : this;
3246
  };
3247
 
3248
  // bind legacy bind/unbind to on/off
3249
  JQLite.prototype.bind = JQLite.prototype.on;
3250
  JQLite.prototype.unbind = JQLite.prototype.off;
3251
});
3252
 
3253
 
3254
// Provider for private $$jqLite service
3255
function $$jqLiteProvider() {
3256
  this.$get = function $$jqLite() {
3257
    return extend(JQLite, {
3258
      hasClass: function(node, classes) {
3259
        if (node.attr) node = node[0];
3260
        return jqLiteHasClass(node, classes);
3261
      },
3262
      addClass: function(node, classes) {
3263
        if (node.attr) node = node[0];
3264
        return jqLiteAddClass(node, classes);
3265
      },
3266
      removeClass: function(node, classes) {
3267
        if (node.attr) node = node[0];
3268
        return jqLiteRemoveClass(node, classes);
3269
      }
3270
    });
3271
  };
3272
}
3273
 
3274
/**
3275
 * Computes a hash of an 'obj'.
3276
 * Hash of a:
3277
 *  string is string
3278
 *  number is number as string
3279
 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3280
 *         that is also assigned to the $$hashKey property of the object.
3281
 *
3282
 * @param obj
3283
 * @returns {string} hash string such that the same input will have the same hash string.
3284
 *         The resulting string key is in 'type:hashKey' format.
3285
 */
3286
function hashKey(obj, nextUidFn) {
3287
  var key = obj && obj.$$hashKey;
3288
 
3289
  if (key) {
3290
    if (typeof key === 'function') {
3291
      key = obj.$$hashKey();
3292
    }
3293
    return key;
3294
  }
3295
 
3296
  var objType = typeof obj;
3297
  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3298
    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3299
  } else {
3300
    key = objType + ':' + obj;
3301
  }
3302
 
3303
  return key;
3304
}
3305
 
3306
/**
3307
 * HashMap which can use objects as keys
3308
 */
3309
function HashMap(array, isolatedUid) {
3310
  if (isolatedUid) {
3311
    var uid = 0;
3312
    this.nextUid = function() {
3313
      return ++uid;
3314
    };
3315
  }
3316
  forEach(array, this.put, this);
3317
}
3318
HashMap.prototype = {
3319
  /**
3320
   * Store key value pair
3321
   * @param key key to store can be any type
3322
   * @param value value to store can be any type
3323
   */
3324
  put: function(key, value) {
3325
    this[hashKey(key, this.nextUid)] = value;
3326
  },
3327
 
3328
  /**
3329
   * @param key
3330
   * @returns {Object} the value for the key
3331
   */
3332
  get: function(key) {
3333
    return this[hashKey(key, this.nextUid)];
3334
  },
3335
 
3336
  /**
3337
   * Remove the key/value pair
3338
   * @param key
3339
   */
3340
  remove: function(key) {
3341
    var value = this[key = hashKey(key, this.nextUid)];
3342
    delete this[key];
3343
    return value;
3344
  }
3345
};
3346
 
3347
/**
3348
 * @ngdoc function
3349
 * @module ng
3350
 * @name angular.injector
3351
 * @kind function
3352
 *
3353
 * @description
3354
 * Creates an injector object that can be used for retrieving services as well as for
3355
 * dependency injection (see {@link guide/di dependency injection}).
3356
 *
3357
 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3358
 *     {@link angular.module}. The `ng` module must be explicitly added.
3359
 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3360
 *     disallows argument name annotation inference.
3361
 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3362
 *
3363
 * @example
3364
 * Typical usage
3365
 * ```js
3366
 *   // create an injector
3367
 *   var $injector = angular.injector(['ng']);
3368
 *
3369
 *   // use the injector to kick off your application
3370
 *   // use the type inference to auto inject arguments, or use implicit injection
3371
 *   $injector.invoke(function($rootScope, $compile, $document) {
3372
 *     $compile($document)($rootScope);
3373
 *     $rootScope.$digest();
3374
 *   });
3375
 * ```
3376
 *
3377
 * Sometimes you want to get access to the injector of a currently running Angular app
3378
 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3379
 * application has been bootstrapped. You can do this using the extra `injector()` added
3380
 * to JQuery/jqLite elements. See {@link angular.element}.
3381
 *
3382
 * *This is fairly rare but could be the case if a third party library is injecting the
3383
 * markup.*
3384
 *
3385
 * In the following example a new block of HTML containing a `ng-controller`
3386
 * directive is added to the end of the document body by JQuery. We then compile and link
3387
 * it into the current AngularJS scope.
3388
 *
3389
 * ```js
3390
 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3391
 * $(document.body).append($div);
3392
 *
3393
 * angular.element(document).injector().invoke(function($compile) {
3394
 *   var scope = angular.element($div).scope();
3395
 *   $compile($div)(scope);
3396
 * });
3397
 * ```
3398
 */
3399
 
3400
 
3401
/**
3402
 * @ngdoc module
3403
 * @name auto
3404
 * @description
3405
 *
3406
 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3407
 */
3408
 
3409
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3410
var FN_ARG_SPLIT = /,/;
3411
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3412
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3413
var $injectorMinErr = minErr('$injector');
3414
 
3415
function anonFn(fn) {
3416
  // For anonymous functions, showing at the very least the function signature can help in
3417
  // debugging.
3418
  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3419
      args = fnText.match(FN_ARGS);
3420
  if (args) {
3421
    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3422
  }
3423
  return 'fn';
3424
}
3425
 
3426
function annotate(fn, strictDi, name) {
3427
  var $inject,
3428
      fnText,
3429
      argDecl,
3430
      last;
3431
 
3432
  if (typeof fn === 'function') {
3433
    if (!($inject = fn.$inject)) {
3434
      $inject = [];
3435
      if (fn.length) {
3436
        if (strictDi) {
3437
          if (!isString(name) || !name) {
3438
            name = fn.name || anonFn(fn);
3439
          }
3440
          throw $injectorMinErr('strictdi',
3441
            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3442
        }
3443
        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3444
        argDecl = fnText.match(FN_ARGS);
3445
        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3446
          arg.replace(FN_ARG, function(all, underscore, name) {
3447
            $inject.push(name);
3448
          });
3449
        });
3450
      }
3451
      fn.$inject = $inject;
3452
    }
3453
  } else if (isArray(fn)) {
3454
    last = fn.length - 1;
3455
    assertArgFn(fn[last], 'fn');
3456
    $inject = fn.slice(0, last);
3457
  } else {
3458
    assertArgFn(fn, 'fn', true);
3459
  }
3460
  return $inject;
3461
}
3462
 
3463
///////////////////////////////////////
3464
 
3465
/**
3466
 * @ngdoc service
3467
 * @name $injector
3468
 *
3469
 * @description
3470
 *
3471
 * `$injector` is used to retrieve object instances as defined by
3472
 * {@link auto.$provide provider}, instantiate types, invoke methods,
3473
 * and load modules.
3474
 *
3475
 * The following always holds true:
3476
 *
3477
 * ```js
3478
 *   var $injector = angular.injector();
3479
 *   expect($injector.get('$injector')).toBe($injector);
3480
 *   expect($injector.invoke(function($injector) {
3481
 *     return $injector;
3482
 *   })).toBe($injector);
3483
 * ```
3484
 *
3485
 * # Injection Function Annotation
3486
 *
3487
 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3488
 * following are all valid ways of annotating function with injection arguments and are equivalent.
3489
 *
3490
 * ```js
3491
 *   // inferred (only works if code not minified/obfuscated)
3492
 *   $injector.invoke(function(serviceA){});
3493
 *
3494
 *   // annotated
3495
 *   function explicit(serviceA) {};
3496
 *   explicit.$inject = ['serviceA'];
3497
 *   $injector.invoke(explicit);
3498
 *
3499
 *   // inline
3500
 *   $injector.invoke(['serviceA', function(serviceA){}]);
3501
 * ```
3502
 *
3503
 * ## Inference
3504
 *
3505
 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3506
 * can then be parsed and the function arguments can be extracted. This method of discovering
3507
 * annotations is disallowed when the injector is in strict mode.
3508
 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3509
 * argument names.
3510
 *
3511
 * ## `$inject` Annotation
3512
 * By adding an `$inject` property onto a function the injection parameters can be specified.
3513
 *
3514
 * ## Inline
3515
 * As an array of injection names, where the last item in the array is the function to call.
3516
 */
3517
 
3518
/**
3519
 * @ngdoc method
3520
 * @name $injector#get
3521
 *
3522
 * @description
3523
 * Return an instance of the service.
3524
 *
3525
 * @param {string} name The name of the instance to retrieve.
3526
 * @param {string} caller An optional string to provide the origin of the function call for error messages.
3527
 * @return {*} The instance.
3528
 */
3529
 
3530
/**
3531
 * @ngdoc method
3532
 * @name $injector#invoke
3533
 *
3534
 * @description
3535
 * Invoke the method and supply the method arguments from the `$injector`.
3536
 *
3537
 * @param {!Function} fn The function to invoke. Function parameters are injected according to the
3538
 *   {@link guide/di $inject Annotation} rules.
3539
 * @param {Object=} self The `this` for the invoked method.
3540
 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3541
 *                         object first, before the `$injector` is consulted.
3542
 * @returns {*} the value returned by the invoked `fn` function.
3543
 */
3544
 
3545
/**
3546
 * @ngdoc method
3547
 * @name $injector#has
3548
 *
3549
 * @description
3550
 * Allows the user to query if the particular service exists.
3551
 *
3552
 * @param {string} name Name of the service to query.
3553
 * @returns {boolean} `true` if injector has given service.
3554
 */
3555
 
3556
/**
3557
 * @ngdoc method
3558
 * @name $injector#instantiate
3559
 * @description
3560
 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3561
 * operator, and supplies all of the arguments to the constructor function as specified by the
3562
 * constructor annotation.
3563
 *
3564
 * @param {Function} Type Annotated constructor function.
3565
 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3566
 * object first, before the `$injector` is consulted.
3567
 * @returns {Object} new instance of `Type`.
3568
 */
3569
 
3570
/**
3571
 * @ngdoc method
3572
 * @name $injector#annotate
3573
 *
3574
 * @description
3575
 * Returns an array of service names which the function is requesting for injection. This API is
3576
 * used by the injector to determine which services need to be injected into the function when the
3577
 * function is invoked. There are three ways in which the function can be annotated with the needed
3578
 * dependencies.
3579
 *
3580
 * # Argument names
3581
 *
3582
 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3583
 * by converting the function into a string using `toString()` method and extracting the argument
3584
 * names.
3585
 * ```js
3586
 *   // Given
3587
 *   function MyController($scope, $route) {
3588
 *     // ...
3589
 *   }
3590
 *
3591
 *   // Then
3592
 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3593
 * ```
3594
 *
3595
 * You can disallow this method by using strict injection mode.
3596
 *
3597
 * This method does not work with code minification / obfuscation. For this reason the following
3598
 * annotation strategies are supported.
3599
 *
3600
 * # The `$inject` property
3601
 *
3602
 * If a function has an `$inject` property and its value is an array of strings, then the strings
3603
 * represent names of services to be injected into the function.
3604
 * ```js
3605
 *   // Given
3606
 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
3607
 *     // ...
3608
 *   }
3609
 *   // Define function dependencies
3610
 *   MyController['$inject'] = ['$scope', '$route'];
3611
 *
3612
 *   // Then
3613
 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3614
 * ```
3615
 *
3616
 * # The array notation
3617
 *
3618
 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3619
 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3620
 * a way that survives minification is a better choice:
3621
 *
3622
 * ```js
3623
 *   // We wish to write this (not minification / obfuscation safe)
3624
 *   injector.invoke(function($compile, $rootScope) {
3625
 *     // ...
3626
 *   });
3627
 *
3628
 *   // We are forced to write break inlining
3629
 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3630
 *     // ...
3631
 *   };
3632
 *   tmpFn.$inject = ['$compile', '$rootScope'];
3633
 *   injector.invoke(tmpFn);
3634
 *
3635
 *   // To better support inline function the inline annotation is supported
3636
 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3637
 *     // ...
3638
 *   }]);
3639
 *
3640
 *   // Therefore
3641
 *   expect(injector.annotate(
3642
 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3643
 *    ).toEqual(['$compile', '$rootScope']);
3644
 * ```
3645
 *
3646
 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3647
 * be retrieved as described above.
3648
 *
3649
 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3650
 *
3651
 * @returns {Array.<string>} The names of the services which the function requires.
3652
 */
3653
 
3654
 
3655
 
3656
 
3657
/**
3658
 * @ngdoc service
3659
 * @name $provide
3660
 *
3661
 * @description
3662
 *
3663
 * The {@link auto.$provide $provide} service has a number of methods for registering components
3664
 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3665
 * {@link angular.Module}.
3666
 *
3667
 * An Angular **service** is a singleton object created by a **service factory**.  These **service
3668
 * factories** are functions which, in turn, are created by a **service provider**.
3669
 * The **service providers** are constructor functions. When instantiated they must contain a
3670
 * property called `$get`, which holds the **service factory** function.
3671
 *
3672
 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3673
 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3674
 * function to get the instance of the **service**.
3675
 *
3676
 * Often services have no configuration options and there is no need to add methods to the service
3677
 * provider.  The provider will be no more than a constructor function with a `$get` property. For
3678
 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3679
 * services without specifying a provider.
3680
 *
3681
 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3682
 *     {@link auto.$injector $injector}
3683
 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3684
 *     providers and services.
3685
 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3686
 *     services, not providers.
3687
 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3688
 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
3689
 *     given factory function.
3690
 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3691
 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3692
 *      a new object using the given constructor function.
3693
 *
3694
 * See the individual methods for more information and examples.
3695
 */
3696
 
3697
/**
3698
 * @ngdoc method
3699
 * @name $provide#provider
3700
 * @description
3701
 *
3702
 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3703
 * are constructor functions, whose instances are responsible for "providing" a factory for a
3704
 * service.
3705
 *
3706
 * Service provider names start with the name of the service they provide followed by `Provider`.
3707
 * For example, the {@link ng.$log $log} service has a provider called
3708
 * {@link ng.$logProvider $logProvider}.
3709
 *
3710
 * Service provider objects can have additional methods which allow configuration of the provider
3711
 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3712
 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3713
 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3714
 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3715
 * console or not.
3716
 *
3717
 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3718
                        'Provider'` key.
3719
 * @param {(Object|function())} provider If the provider is:
3720
 *
3721
 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3722
 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3723
 *   - `Constructor`: a new instance of the provider will be created using
3724
 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3725
 *
3726
 * @returns {Object} registered provider instance
3727
 
3728
 * @example
3729
 *
3730
 * The following example shows how to create a simple event tracking service and register it using
3731
 * {@link auto.$provide#provider $provide.provider()}.
3732
 *
3733
 * ```js
3734
 *  // Define the eventTracker provider
3735
 *  function EventTrackerProvider() {
3736
 *    var trackingUrl = '/track';
3737
 *
3738
 *    // A provider method for configuring where the tracked events should been saved
3739
 *    this.setTrackingUrl = function(url) {
3740
 *      trackingUrl = url;
3741
 *    };
3742
 *
3743
 *    // The service factory function
3744
 *    this.$get = ['$http', function($http) {
3745
 *      var trackedEvents = {};
3746
 *      return {
3747
 *        // Call this to track an event
3748
 *        event: function(event) {
3749
 *          var count = trackedEvents[event] || 0;
3750
 *          count += 1;
3751
 *          trackedEvents[event] = count;
3752
 *          return count;
3753
 *        },
3754
 *        // Call this to save the tracked events to the trackingUrl
3755
 *        save: function() {
3756
 *          $http.post(trackingUrl, trackedEvents);
3757
 *        }
3758
 *      };
3759
 *    }];
3760
 *  }
3761
 *
3762
 *  describe('eventTracker', function() {
3763
 *    var postSpy;
3764
 *
3765
 *    beforeEach(module(function($provide) {
3766
 *      // Register the eventTracker provider
3767
 *      $provide.provider('eventTracker', EventTrackerProvider);
3768
 *    }));
3769
 *
3770
 *    beforeEach(module(function(eventTrackerProvider) {
3771
 *      // Configure eventTracker provider
3772
 *      eventTrackerProvider.setTrackingUrl('/custom-track');
3773
 *    }));
3774
 *
3775
 *    it('tracks events', inject(function(eventTracker) {
3776
 *      expect(eventTracker.event('login')).toEqual(1);
3777
 *      expect(eventTracker.event('login')).toEqual(2);
3778
 *    }));
3779
 *
3780
 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
3781
 *      postSpy = spyOn($http, 'post');
3782
 *      eventTracker.event('login');
3783
 *      eventTracker.save();
3784
 *      expect(postSpy).toHaveBeenCalled();
3785
 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3786
 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3787
 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3788
 *    }));
3789
 *  });
3790
 * ```
3791
 */
3792
 
3793
/**
3794
 * @ngdoc method
3795
 * @name $provide#factory
3796
 * @description
3797
 *
3798
 * Register a **service factory**, which will be called to return the service instance.
3799
 * This is short for registering a service where its provider consists of only a `$get` property,
3800
 * which is the given service factory function.
3801
 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3802
 * configure your service in a provider.
3803
 *
3804
 * @param {string} name The name of the instance.
3805
 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
3806
 *                            for `$provide.provider(name, {$get: $getFn})`.
3807
 * @returns {Object} registered provider instance
3808
 *
3809
 * @example
3810
 * Here is an example of registering a service
3811
 * ```js
3812
 *   $provide.factory('ping', ['$http', function($http) {
3813
 *     return function ping() {
3814
 *       return $http.send('/ping');
3815
 *     };
3816
 *   }]);
3817
 * ```
3818
 * You would then inject and use this service like this:
3819
 * ```js
3820
 *   someModule.controller('Ctrl', ['ping', function(ping) {
3821
 *     ping();
3822
 *   }]);
3823
 * ```
3824
 */
3825
 
3826
 
3827
/**
3828
 * @ngdoc method
3829
 * @name $provide#service
3830
 * @description
3831
 *
3832
 * Register a **service constructor**, which will be invoked with `new` to create the service
3833
 * instance.
3834
 * This is short for registering a service where its provider's `$get` property is the service
3835
 * constructor function that will be used to instantiate the service instance.
3836
 *
3837
 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3838
 * as a type/class.
3839
 *
3840
 * @param {string} name The name of the instance.
3841
 * @param {Function} constructor A class (constructor function) that will be instantiated.
3842
 * @returns {Object} registered provider instance
3843
 *
3844
 * @example
3845
 * Here is an example of registering a service using
3846
 * {@link auto.$provide#service $provide.service(class)}.
3847
 * ```js
3848
 *   var Ping = function($http) {
3849
 *     this.$http = $http;
3850
 *   };
3851
 *
3852
 *   Ping.$inject = ['$http'];
3853
 *
3854
 *   Ping.prototype.send = function() {
3855
 *     return this.$http.get('/ping');
3856
 *   };
3857
 *   $provide.service('ping', Ping);
3858
 * ```
3859
 * You would then inject and use this service like this:
3860
 * ```js
3861
 *   someModule.controller('Ctrl', ['ping', function(ping) {
3862
 *     ping.send();
3863
 *   }]);
3864
 * ```
3865
 */
3866
 
3867
 
3868
/**
3869
 * @ngdoc method
3870
 * @name $provide#value
3871
 * @description
3872
 *
3873
 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3874
 * number, an array, an object or a function.  This is short for registering a service where its
3875
 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3876
 * service**.
3877
 *
3878
 * Value services are similar to constant services, except that they cannot be injected into a
3879
 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3880
 * an Angular
3881
 * {@link auto.$provide#decorator decorator}.
3882
 *
3883
 * @param {string} name The name of the instance.
3884
 * @param {*} value The value.
3885
 * @returns {Object} registered provider instance
3886
 *
3887
 * @example
3888
 * Here are some examples of creating value services.
3889
 * ```js
3890
 *   $provide.value('ADMIN_USER', 'admin');
3891
 *
3892
 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3893
 *
3894
 *   $provide.value('halfOf', function(value) {
3895
 *     return value / 2;
3896
 *   });
3897
 * ```
3898
 */
3899
 
3900
 
3901
/**
3902
 * @ngdoc method
3903
 * @name $provide#constant
3904
 * @description
3905
 *
3906
 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3907
 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3908
 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3909
 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3910
 *
3911
 * @param {string} name The name of the constant.
3912
 * @param {*} value The constant value.
3913
 * @returns {Object} registered instance
3914
 *
3915
 * @example
3916
 * Here a some examples of creating constants:
3917
 * ```js
3918
 *   $provide.constant('SHARD_HEIGHT', 306);
3919
 *
3920
 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3921
 *
3922
 *   $provide.constant('double', function(value) {
3923
 *     return value * 2;
3924
 *   });
3925
 * ```
3926
 */
3927
 
3928
 
3929
/**
3930
 * @ngdoc method
3931
 * @name $provide#decorator
3932
 * @description
3933
 *
3934
 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3935
 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3936
 * service. The object returned by the decorator may be the original service, or a new service
3937
 * object which replaces or wraps and delegates to the original service.
3938
 *
3939
 * @param {string} name The name of the service to decorate.
3940
 * @param {function()} decorator This function will be invoked when the service needs to be
3941
 *    instantiated and should return the decorated service instance. The function is called using
3942
 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3943
 *    Local injection arguments:
3944
 *
3945
 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
3946
 *      decorated or delegated to.
3947
 *
3948
 * @example
3949
 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
3950
 * calls to {@link ng.$log#error $log.warn()}.
3951
 * ```js
3952
 *   $provide.decorator('$log', ['$delegate', function($delegate) {
3953
 *     $delegate.warn = $delegate.error;
3954
 *     return $delegate;
3955
 *   }]);
3956
 * ```
3957
 */
3958
 
3959
 
3960
function createInjector(modulesToLoad, strictDi) {
3961
  strictDi = (strictDi === true);
3962
  var INSTANTIATING = {},
3963
      providerSuffix = 'Provider',
3964
      path = [],
3965
      loadedModules = new HashMap([], true),
3966
      providerCache = {
3967
        $provide: {
3968
            provider: supportObject(provider),
3969
            factory: supportObject(factory),
3970
            service: supportObject(service),
3971
            value: supportObject(value),
3972
            constant: supportObject(constant),
3973
            decorator: decorator
3974
          }
3975
      },
3976
      providerInjector = (providerCache.$injector =
3977
          createInternalInjector(providerCache, function(serviceName, caller) {
3978
            if (angular.isString(caller)) {
3979
              path.push(caller);
3980
            }
3981
            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
3982
          })),
3983
      instanceCache = {},
3984
      instanceInjector = (instanceCache.$injector =
3985
          createInternalInjector(instanceCache, function(serviceName, caller) {
3986
            var provider = providerInjector.get(serviceName + providerSuffix, caller);
3987
            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
3988
          }));
3989
 
3990
 
3991
  forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
3992
 
3993
  return instanceInjector;
3994
 
3995
  ////////////////////////////////////
3996
  // $provider
3997
  ////////////////////////////////////
3998
 
3999
  function supportObject(delegate) {
4000
    return function(key, value) {
4001
      if (isObject(key)) {
4002
        forEach(key, reverseParams(delegate));
4003
      } else {
4004
        return delegate(key, value);
4005
      }
4006
    };
4007
  }
4008
 
4009
  function provider(name, provider_) {
4010
    assertNotHasOwnProperty(name, 'service');
4011
    if (isFunction(provider_) || isArray(provider_)) {
4012
      provider_ = providerInjector.instantiate(provider_);
4013
    }
4014
    if (!provider_.$get) {
4015
      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4016
    }
4017
    return providerCache[name + providerSuffix] = provider_;
4018
  }
4019
 
4020
  function enforceReturnValue(name, factory) {
4021
    return function enforcedReturnValue() {
4022
      var result = instanceInjector.invoke(factory, this);
4023
      if (isUndefined(result)) {
4024
        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4025
      }
4026
      return result;
4027
    };
4028
  }
4029
 
4030
  function factory(name, factoryFn, enforce) {
4031
    return provider(name, {
4032
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4033
    });
4034
  }
4035
 
4036
  function service(name, constructor) {
4037
    return factory(name, ['$injector', function($injector) {
4038
      return $injector.instantiate(constructor);
4039
    }]);
4040
  }
4041
 
4042
  function value(name, val) { return factory(name, valueFn(val), false); }
4043
 
4044
  function constant(name, value) {
4045
    assertNotHasOwnProperty(name, 'constant');
4046
    providerCache[name] = value;
4047
    instanceCache[name] = value;
4048
  }
4049
 
4050
  function decorator(serviceName, decorFn) {
4051
    var origProvider = providerInjector.get(serviceName + providerSuffix),
4052
        orig$get = origProvider.$get;
4053
 
4054
    origProvider.$get = function() {
4055
      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4056
      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4057
    };
4058
  }
4059
 
4060
  ////////////////////////////////////
4061
  // Module Loading
4062
  ////////////////////////////////////
4063
  function loadModules(modulesToLoad) {
4064
    var runBlocks = [], moduleFn;
4065
    forEach(modulesToLoad, function(module) {
4066
      if (loadedModules.get(module)) return;
4067
      loadedModules.put(module, true);
4068
 
4069
      function runInvokeQueue(queue) {
4070
        var i, ii;
4071
        for (i = 0, ii = queue.length; i < ii; i++) {
4072
          var invokeArgs = queue[i],
4073
              provider = providerInjector.get(invokeArgs[0]);
4074
 
4075
          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4076
        }
4077
      }
4078
 
4079
      try {
4080
        if (isString(module)) {
4081
          moduleFn = angularModule(module);
4082
          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4083
          runInvokeQueue(moduleFn._invokeQueue);
4084
          runInvokeQueue(moduleFn._configBlocks);
4085
        } else if (isFunction(module)) {
4086
            runBlocks.push(providerInjector.invoke(module));
4087
        } else if (isArray(module)) {
4088
            runBlocks.push(providerInjector.invoke(module));
4089
        } else {
4090
          assertArgFn(module, 'module');
4091
        }
4092
      } catch (e) {
4093
        if (isArray(module)) {
4094
          module = module[module.length - 1];
4095
        }
4096
        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4097
          // Safari & FF's stack traces don't contain error.message content
4098
          // unlike those of Chrome and IE
4099
          // So if stack doesn't contain message, we create a new string that contains both.
4100
          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4101
          /* jshint -W022 */
4102
          e = e.message + '\n' + e.stack;
4103
        }
4104
        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4105
                  module, e.stack || e.message || e);
4106
      }
4107
    });
4108
    return runBlocks;
4109
  }
4110
 
4111
  ////////////////////////////////////
4112
  // internal Injector
4113
  ////////////////////////////////////
4114
 
4115
  function createInternalInjector(cache, factory) {
4116
 
4117
    function getService(serviceName, caller) {
4118
      if (cache.hasOwnProperty(serviceName)) {
4119
        if (cache[serviceName] === INSTANTIATING) {
4120
          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4121
                    serviceName + ' <- ' + path.join(' <- '));
4122
        }
4123
        return cache[serviceName];
4124
      } else {
4125
        try {
4126
          path.unshift(serviceName);
4127
          cache[serviceName] = INSTANTIATING;
4128
          return cache[serviceName] = factory(serviceName, caller);
4129
        } catch (err) {
4130
          if (cache[serviceName] === INSTANTIATING) {
4131
            delete cache[serviceName];
4132
          }
4133
          throw err;
4134
        } finally {
4135
          path.shift();
4136
        }
4137
      }
4138
    }
4139
 
4140
    function invoke(fn, self, locals, serviceName) {
4141
      if (typeof locals === 'string') {
4142
        serviceName = locals;
4143
        locals = null;
4144
      }
4145
 
4146
      var args = [],
4147
          $inject = annotate(fn, strictDi, serviceName),
4148
          length, i,
4149
          key;
4150
 
4151
      for (i = 0, length = $inject.length; i < length; i++) {
4152
        key = $inject[i];
4153
        if (typeof key !== 'string') {
4154
          throw $injectorMinErr('itkn',
4155
                  'Incorrect injection token! Expected service name as string, got {0}', key);
4156
        }
4157
        args.push(
4158
          locals && locals.hasOwnProperty(key)
4159
          ? locals[key]
4160
          : getService(key, serviceName)
4161
        );
4162
      }
4163
      if (isArray(fn)) {
4164
        fn = fn[length];
4165
      }
4166
 
4167
      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4168
      // #5388
4169
      return fn.apply(self, args);
4170
    }
4171
 
4172
    function instantiate(Type, locals, serviceName) {
4173
      // Check if Type is annotated and use just the given function at n-1 as parameter
4174
      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4175
      // Object creation: http://jsperf.com/create-constructor/2
4176
      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype);
4177
      var returnedValue = invoke(Type, instance, locals, serviceName);
4178
 
4179
      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4180
    }
4181
 
4182
    return {
4183
      invoke: invoke,
4184
      instantiate: instantiate,
4185
      get: getService,
4186
      annotate: annotate,
4187
      has: function(name) {
4188
        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4189
      }
4190
    };
4191
  }
4192
}
4193
 
4194
createInjector.$$annotate = annotate;
4195
 
4196
/**
4197
 * @ngdoc provider
4198
 * @name $anchorScrollProvider
4199
 *
4200
 * @description
4201
 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4202
 * {@link ng.$location#hash $location.hash()} changes.
4203
 */
4204
function $AnchorScrollProvider() {
4205
 
4206
  var autoScrollingEnabled = true;
4207
 
4208
  /**
4209
   * @ngdoc method
4210
   * @name $anchorScrollProvider#disableAutoScrolling
4211
   *
4212
   * @description
4213
   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4214
   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4215
   * Use this method to disable automatic scrolling.
4216
   *
4217
   * If automatic scrolling is disabled, one must explicitly call
4218
   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4219
   * current hash.
4220
   */
4221
  this.disableAutoScrolling = function() {
4222
    autoScrollingEnabled = false;
4223
  };
4224
 
4225
  /**
4226
   * @ngdoc service
4227
   * @name $anchorScroll
4228
   * @kind function
4229
   * @requires $window
4230
   * @requires $location
4231
   * @requires $rootScope
4232
   *
4233
   * @description
4234
   * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
4235
   * scrolls to the related element, according to the rules specified in the
4236
   * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4237
   *
4238
   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4239
   * match any anchor whenever it changes. This can be disabled by calling
4240
   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4241
   *
4242
   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4243
   * vertical scroll-offset (either fixed or dynamic).
4244
   *
4245
   * @property {(number|function|jqLite)} yOffset
4246
   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4247
   * positioned elements at the top of the page, such as navbars, headers etc.
4248
   *
4249
   * `yOffset` can be specified in various ways:
4250
   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4251
   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4252
   *   a number representing the offset (in pixels).<br /><br />
4253
   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4254
   *   the top of the page to the element's bottom will be used as offset.<br />
4255
   *   **Note**: The element will be taken into account only as long as its `position` is set to
4256
   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4257
   *   their height and/or positioning according to the viewport's size.
4258
   *
4259
   * <br />
4260
   * <div class="alert alert-warning">
4261
   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4262
   * not some child element.
4263
   * </div>
4264
   *
4265
   * @example
4266
     <example module="anchorScrollExample">
4267
       <file name="index.html">
4268
         <div id="scrollArea" ng-controller="ScrollController">
4269
           <a ng-click="gotoBottom()">Go to bottom</a>
4270
           <a id="bottom"></a> You're at the bottom!
4271
         </div>
4272
       </file>
4273
       <file name="script.js">
4274
         angular.module('anchorScrollExample', [])
4275
           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4276
             function ($scope, $location, $anchorScroll) {
4277
               $scope.gotoBottom = function() {
4278
                 // set the location.hash to the id of
4279
                 // the element you wish to scroll to.
4280
                 $location.hash('bottom');
4281
 
4282
                 // call $anchorScroll()
4283
                 $anchorScroll();
4284
               };
4285
             }]);
4286
       </file>
4287
       <file name="style.css">
4288
         #scrollArea {
4289
           height: 280px;
4290
           overflow: auto;
4291
         }
4292
 
4293
         #bottom {
4294
           display: block;
4295
           margin-top: 2000px;
4296
         }
4297
       </file>
4298
     </example>
4299
   *
4300
   * <hr />
4301
   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4302
   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4303
   *
4304
   * @example
4305
     <example module="anchorScrollOffsetExample">
4306
       <file name="index.html">
4307
         <div class="fixed-header" ng-controller="headerCtrl">
4308
           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4309
             Go to anchor {{x}}
4310
           </a>
4311
         </div>
4312
         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4313
           Anchor {{x}} of 5
4314
         </div>
4315
       </file>
4316
       <file name="script.js">
4317
         angular.module('anchorScrollOffsetExample', [])
4318
           .run(['$anchorScroll', function($anchorScroll) {
4319
             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4320
           }])
4321
           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4322
             function ($anchorScroll, $location, $scope) {
4323
               $scope.gotoAnchor = function(x) {
4324
                 var newHash = 'anchor' + x;
4325
                 if ($location.hash() !== newHash) {
4326
                   // set the $location.hash to `newHash` and
4327
                   // $anchorScroll will automatically scroll to it
4328
                   $location.hash('anchor' + x);
4329
                 } else {
4330
                   // call $anchorScroll() explicitly,
4331
                   // since $location.hash hasn't changed
4332
                   $anchorScroll();
4333
                 }
4334
               };
4335
             }
4336
           ]);
4337
       </file>
4338
       <file name="style.css">
4339
         body {
4340
           padding-top: 50px;
4341
         }
4342
 
4343
         .anchor {
4344
           border: 2px dashed DarkOrchid;
4345
           padding: 10px 10px 200px 10px;
4346
         }
4347
 
4348
         .fixed-header {
4349
           background-color: rgba(0, 0, 0, 0.2);
4350
           height: 50px;
4351
           position: fixed;
4352
           top: 0; left: 0; right: 0;
4353
         }
4354
 
4355
         .fixed-header > a {
4356
           display: inline-block;
4357
           margin: 5px 15px;
4358
         }
4359
       </file>
4360
     </example>
4361
   */
4362
  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4363
    var document = $window.document;
4364
 
4365
    // Helper function to get first anchor from a NodeList
4366
    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4367
    //  and working in all supported browsers.)
4368
    function getFirstAnchor(list) {
4369
      var result = null;
4370
      Array.prototype.some.call(list, function(element) {
4371
        if (nodeName_(element) === 'a') {
4372
          result = element;
4373
          return true;
4374
        }
4375
      });
4376
      return result;
4377
    }
4378
 
4379
    function getYOffset() {
4380
 
4381
      var offset = scroll.yOffset;
4382
 
4383
      if (isFunction(offset)) {
4384
        offset = offset();
4385
      } else if (isElement(offset)) {
4386
        var elem = offset[0];
4387
        var style = $window.getComputedStyle(elem);
4388
        if (style.position !== 'fixed') {
4389
          offset = 0;
4390
        } else {
4391
          offset = elem.getBoundingClientRect().bottom;
4392
        }
4393
      } else if (!isNumber(offset)) {
4394
        offset = 0;
4395
      }
4396
 
4397
      return offset;
4398
    }
4399
 
4400
    function scrollTo(elem) {
4401
      if (elem) {
4402
        elem.scrollIntoView();
4403
 
4404
        var offset = getYOffset();
4405
 
4406
        if (offset) {
4407
          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4408
          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4409
          // top of the viewport.
4410
          //
4411
          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4412
          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4413
          // way down the page.
4414
          //
4415
          // This is often the case for elements near the bottom of the page.
4416
          //
4417
          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4418
          // the top of the element and the offset, which is enough to align the top of `elem` at the
4419
          // desired position.
4420
          var elemTop = elem.getBoundingClientRect().top;
4421
          $window.scrollBy(0, elemTop - offset);
4422
        }
4423
      } else {
4424
        $window.scrollTo(0, 0);
4425
      }
4426
    }
4427
 
4428
    function scroll() {
4429
      var hash = $location.hash(), elm;
4430
 
4431
      // empty hash, scroll to the top of the page
4432
      if (!hash) scrollTo(null);
4433
 
4434
      // element with given id
4435
      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4436
 
4437
      // first anchor with given name :-D
4438
      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4439
 
4440
      // no element and hash == 'top', scroll to the top of the page
4441
      else if (hash === 'top') scrollTo(null);
4442
    }
4443
 
4444
    // does not scroll when user clicks on anchor link that is currently on
4445
    // (no url change, no $location.hash() change), browser native does scroll
4446
    if (autoScrollingEnabled) {
4447
      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4448
        function autoScrollWatchAction(newVal, oldVal) {
4449
          // skip the initial scroll if $location.hash is empty
4450
          if (newVal === oldVal && newVal === '') return;
4451
 
4452
          jqLiteDocumentLoaded(function() {
4453
            $rootScope.$evalAsync(scroll);
4454
          });
4455
        });
4456
    }
4457
 
4458
    return scroll;
4459
  }];
4460
}
4461
 
4462
var $animateMinErr = minErr('$animate');
4463
 
4464
/**
4465
 * @ngdoc provider
4466
 * @name $animateProvider
4467
 *
4468
 * @description
4469
 * Default implementation of $animate that doesn't perform any animations, instead just
4470
 * synchronously performs DOM
4471
 * updates and calls done() callbacks.
4472
 *
4473
 * In order to enable animations the ngAnimate module has to be loaded.
4474
 *
4475
 * To see the functional implementation check out src/ngAnimate/animate.js
4476
 */
4477
var $AnimateProvider = ['$provide', function($provide) {
4478
 
4479
 
4480
  this.$$selectors = {};
4481
 
4482
 
4483
  /**
4484
   * @ngdoc method
4485
   * @name $animateProvider#register
4486
   *
4487
   * @description
4488
   * Registers a new injectable animation factory function. The factory function produces the
4489
   * animation object which contains callback functions for each event that is expected to be
4490
   * animated.
4491
   *
4492
   *   * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4493
   *   must be called once the element animation is complete. If a function is returned then the
4494
   *   animation service will use this function to cancel the animation whenever a cancel event is
4495
   *   triggered.
4496
   *
4497
   *
4498
   * ```js
4499
   *   return {
4500
     *     eventFn : function(element, done) {
4501
     *       //code to run the animation
4502
     *       //once complete, then run done()
4503
     *       return function cancellationFunction() {
4504
     *         //code to cancel the animation
4505
     *       }
4506
     *     }
4507
     *   }
4508
   * ```
4509
   *
4510
   * @param {string} name The name of the animation.
4511
   * @param {Function} factory The factory function that will be executed to return the animation
4512
   *                           object.
4513
   */
4514
  this.register = function(name, factory) {
4515
    var key = name + '-animation';
4516
    if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4517
        "Expecting class selector starting with '.' got '{0}'.", name);
4518
    this.$$selectors[name.substr(1)] = key;
4519
    $provide.factory(key, factory);
4520
  };
4521
 
4522
  /**
4523
   * @ngdoc method
4524
   * @name $animateProvider#classNameFilter
4525
   *
4526
   * @description
4527
   * Sets and/or returns the CSS class regular expression that is checked when performing
4528
   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4529
   * therefore enable $animate to attempt to perform an animation on any element.
4530
   * When setting the classNameFilter value, animations will only be performed on elements
4531
   * that successfully match the filter expression. This in turn can boost performance
4532
   * for low-powered devices as well as applications containing a lot of structural operations.
4533
   * @param {RegExp=} expression The className expression which will be checked against all animations
4534
   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4535
   */
4536
  this.classNameFilter = function(expression) {
4537
    if (arguments.length === 1) {
4538
      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4539
    }
4540
    return this.$$classNameFilter;
4541
  };
4542
 
4543
  this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
4544
 
4545
    var currentDefer;
4546
 
4547
    function runAnimationPostDigest(fn) {
4548
      var cancelFn, defer = $$q.defer();
4549
      defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
4550
        cancelFn && cancelFn();
4551
      };
4552
 
4553
      $rootScope.$$postDigest(function ngAnimatePostDigest() {
4554
        cancelFn = fn(function ngAnimateNotifyComplete() {
4555
          defer.resolve();
4556
        });
4557
      });
4558
 
4559
      return defer.promise;
4560
    }
4561
 
4562
    function resolveElementClasses(element, classes) {
4563
      var toAdd = [], toRemove = [];
4564
 
4565
      var hasClasses = createMap();
4566
      forEach((element.attr('class') || '').split(/\s+/), function(className) {
4567
        hasClasses[className] = true;
4568
      });
4569
 
4570
      forEach(classes, function(status, className) {
4571
        var hasClass = hasClasses[className];
4572
 
4573
        // If the most recent class manipulation (via $animate) was to remove the class, and the
4574
        // element currently has the class, the class is scheduled for removal. Otherwise, if
4575
        // the most recent class manipulation (via $animate) was to add the class, and the
4576
        // element does not currently have the class, the class is scheduled to be added.
4577
        if (status === false && hasClass) {
4578
          toRemove.push(className);
4579
        } else if (status === true && !hasClass) {
4580
          toAdd.push(className);
4581
        }
4582
      });
4583
 
4584
      return (toAdd.length + toRemove.length) > 0 &&
4585
        [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
4586
    }
4587
 
4588
    function cachedClassManipulation(cache, classes, op) {
4589
      for (var i=0, ii = classes.length; i < ii; ++i) {
4590
        var className = classes[i];
4591
        cache[className] = op;
4592
      }
4593
    }
4594
 
4595
    function asyncPromise() {
4596
      // only serve one instance of a promise in order to save CPU cycles
4597
      if (!currentDefer) {
4598
        currentDefer = $$q.defer();
4599
        $$asyncCallback(function() {
4600
          currentDefer.resolve();
4601
          currentDefer = null;
4602
        });
4603
      }
4604
      return currentDefer.promise;
4605
    }
4606
 
4607
    function applyStyles(element, options) {
4608
      if (angular.isObject(options)) {
4609
        var styles = extend(options.from || {}, options.to || {});
4610
        element.css(styles);
4611
      }
4612
    }
4613
 
4614
    /**
4615
     *
4616
     * @ngdoc service
4617
     * @name $animate
4618
     * @description The $animate service provides rudimentary DOM manipulation functions to
4619
     * insert, remove and move elements within the DOM, as well as adding and removing classes.
4620
     * This service is the core service used by the ngAnimate $animator service which provides
4621
     * high-level animation hooks for CSS and JavaScript.
4622
     *
4623
     * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4624
     * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4625
     * manipulation operations.
4626
     *
4627
     * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4628
     * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4629
     * page}.
4630
     */
4631
    return {
4632
      animate: function(element, from, to) {
4633
        applyStyles(element, { from: from, to: to });
4634
        return asyncPromise();
4635
      },
4636
 
4637
      /**
4638
       *
4639
       * @ngdoc method
4640
       * @name $animate#enter
4641
       * @kind function
4642
       * @description Inserts the element into the DOM either after the `after` element or
4643
       * as the first child within the `parent` element. When the function is called a promise
4644
       * is returned that will be resolved at a later time.
4645
       * @param {DOMElement} element the element which will be inserted into the DOM
4646
       * @param {DOMElement} parent the parent element which will append the element as
4647
       *   a child (if the after element is not present)
4648
       * @param {DOMElement} after the sibling element which will append the element
4649
       *   after itself
4650
       * @param {object=} options an optional collection of styles that will be applied to the element.
4651
       * @return {Promise} the animation callback promise
4652
       */
4653
      enter: function(element, parent, after, options) {
4654
        applyStyles(element, options);
4655
        after ? after.after(element)
4656
              : parent.prepend(element);
4657
        return asyncPromise();
4658
      },
4659
 
4660
      /**
4661
       *
4662
       * @ngdoc method
4663
       * @name $animate#leave
4664
       * @kind function
4665
       * @description Removes the element from the DOM. When the function is called a promise
4666
       * is returned that will be resolved at a later time.
4667
       * @param {DOMElement} element the element which will be removed from the DOM
4668
       * @param {object=} options an optional collection of options that will be applied to the element.
4669
       * @return {Promise} the animation callback promise
4670
       */
4671
      leave: function(element, options) {
4672
        element.remove();
4673
        return asyncPromise();
4674
      },
4675
 
4676
      /**
4677
       *
4678
       * @ngdoc method
4679
       * @name $animate#move
4680
       * @kind function
4681
       * @description Moves the position of the provided element within the DOM to be placed
4682
       * either after the `after` element or inside of the `parent` element. When the function
4683
       * is called a promise is returned that will be resolved at a later time.
4684
       *
4685
       * @param {DOMElement} element the element which will be moved around within the
4686
       *   DOM
4687
       * @param {DOMElement} parent the parent element where the element will be
4688
       *   inserted into (if the after element is not present)
4689
       * @param {DOMElement} after the sibling element where the element will be
4690
       *   positioned next to
4691
       * @param {object=} options an optional collection of options that will be applied to the element.
4692
       * @return {Promise} the animation callback promise
4693
       */
4694
      move: function(element, parent, after, options) {
4695
        // Do not remove element before insert. Removing will cause data associated with the
4696
        // element to be dropped. Insert will implicitly do the remove.
4697
        return this.enter(element, parent, after, options);
4698
      },
4699
 
4700
      /**
4701
       *
4702
       * @ngdoc method
4703
       * @name $animate#addClass
4704
       * @kind function
4705
       * @description Adds the provided className CSS class value to the provided element.
4706
       * When the function is called a promise is returned that will be resolved at a later time.
4707
       * @param {DOMElement} element the element which will have the className value
4708
       *   added to it
4709
       * @param {string} className the CSS class which will be added to the element
4710
       * @param {object=} options an optional collection of options that will be applied to the element.
4711
       * @return {Promise} the animation callback promise
4712
       */
4713
      addClass: function(element, className, options) {
4714
        return this.setClass(element, className, [], options);
4715
      },
4716
 
4717
      $$addClassImmediately: function(element, className, options) {
4718
        element = jqLite(element);
4719
        className = !isString(className)
4720
                        ? (isArray(className) ? className.join(' ') : '')
4721
                        : className;
4722
        forEach(element, function(element) {
4723
          jqLiteAddClass(element, className);
4724
        });
4725
        applyStyles(element, options);
4726
        return asyncPromise();
4727
      },
4728
 
4729
      /**
4730
       *
4731
       * @ngdoc method
4732
       * @name $animate#removeClass
4733
       * @kind function
4734
       * @description Removes the provided className CSS class value from the provided element.
4735
       * When the function is called a promise is returned that will be resolved at a later time.
4736
       * @param {DOMElement} element the element which will have the className value
4737
       *   removed from it
4738
       * @param {string} className the CSS class which will be removed from the element
4739
       * @param {object=} options an optional collection of options that will be applied to the element.
4740
       * @return {Promise} the animation callback promise
4741
       */
4742
      removeClass: function(element, className, options) {
4743
        return this.setClass(element, [], className, options);
4744
      },
4745
 
4746
      $$removeClassImmediately: function(element, className, options) {
4747
        element = jqLite(element);
4748
        className = !isString(className)
4749
                        ? (isArray(className) ? className.join(' ') : '')
4750
                        : className;
4751
        forEach(element, function(element) {
4752
          jqLiteRemoveClass(element, className);
4753
        });
4754
        applyStyles(element, options);
4755
        return asyncPromise();
4756
      },
4757
 
4758
      /**
4759
       *
4760
       * @ngdoc method
4761
       * @name $animate#setClass
4762
       * @kind function
4763
       * @description Adds and/or removes the given CSS classes to and from the element.
4764
       * When the function is called a promise is returned that will be resolved at a later time.
4765
       * @param {DOMElement} element the element which will have its CSS classes changed
4766
       *   removed from it
4767
       * @param {string} add the CSS classes which will be added to the element
4768
       * @param {string} remove the CSS class which will be removed from the element
4769
       * @param {object=} options an optional collection of options that will be applied to the element.
4770
       * @return {Promise} the animation callback promise
4771
       */
4772
      setClass: function(element, add, remove, options) {
4773
        var self = this;
4774
        var STORAGE_KEY = '$$animateClasses';
4775
        var createdCache = false;
4776
        element = jqLite(element);
4777
 
4778
        var cache = element.data(STORAGE_KEY);
4779
        if (!cache) {
4780
          cache = {
4781
            classes: {},
4782
            options: options
4783
          };
4784
          createdCache = true;
4785
        } else if (options && cache.options) {
4786
          cache.options = angular.extend(cache.options || {}, options);
4787
        }
4788
 
4789
        var classes = cache.classes;
4790
 
4791
        add = isArray(add) ? add : add.split(' ');
4792
        remove = isArray(remove) ? remove : remove.split(' ');
4793
        cachedClassManipulation(classes, add, true);
4794
        cachedClassManipulation(classes, remove, false);
4795
 
4796
        if (createdCache) {
4797
          cache.promise = runAnimationPostDigest(function(done) {
4798
            var cache = element.data(STORAGE_KEY);
4799
            element.removeData(STORAGE_KEY);
4800
 
4801
            // in the event that the element is removed before postDigest
4802
            // is run then the cache will be undefined and there will be
4803
            // no need anymore to add or remove and of the element classes
4804
            if (cache) {
4805
              var classes = resolveElementClasses(element, cache.classes);
4806
              if (classes) {
4807
                self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
4808
              }
4809
            }
4810
 
4811
            done();
4812
          });
4813
          element.data(STORAGE_KEY, cache);
4814
        }
4815
 
4816
        return cache.promise;
4817
      },
4818
 
4819
      $$setClassImmediately: function(element, add, remove, options) {
4820
        add && this.$$addClassImmediately(element, add);
4821
        remove && this.$$removeClassImmediately(element, remove);
4822
        applyStyles(element, options);
4823
        return asyncPromise();
4824
      },
4825
 
4826
      enabled: noop,
4827
      cancel: noop
4828
    };
4829
  }];
4830
}];
4831
 
4832
function $$AsyncCallbackProvider() {
4833
  this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4834
    return $$rAF.supported
4835
      ? function(fn) { return $$rAF(fn); }
4836
      : function(fn) {
4837
        return $timeout(fn, 0, false);
4838
      };
4839
  }];
4840
}
4841
 
4842
/* global stripHash: true */
4843
 
4844
/**
4845
 * ! This is a private undocumented service !
4846
 *
4847
 * @name $browser
4848
 * @requires $log
4849
 * @description
4850
 * This object has two goals:
4851
 *
4852
 * - hide all the global state in the browser caused by the window object
4853
 * - abstract away all the browser specific features and inconsistencies
4854
 *
4855
 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4856
 * service, which can be used for convenient testing of the application without the interaction with
4857
 * the real browser apis.
4858
 */
4859
/**
4860
 * @param {object} window The global window object.
4861
 * @param {object} document jQuery wrapped document.
4862
 * @param {object} $log window.console or an object with the same interface.
4863
 * @param {object} $sniffer $sniffer service
4864
 */
4865
function Browser(window, document, $log, $sniffer) {
4866
  var self = this,
4867
      rawDocument = document[0],
4868
      location = window.location,
4869
      history = window.history,
4870
      setTimeout = window.setTimeout,
4871
      clearTimeout = window.clearTimeout,
4872
      pendingDeferIds = {};
4873
 
4874
  self.isMock = false;
4875
 
4876
  var outstandingRequestCount = 0;
4877
  var outstandingRequestCallbacks = [];
4878
 
4879
  // TODO(vojta): remove this temporary api
4880
  self.$$completeOutstandingRequest = completeOutstandingRequest;
4881
  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4882
 
4883
  /**
4884
   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4885
   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4886
   */
4887
  function completeOutstandingRequest(fn) {
4888
    try {
4889
      fn.apply(null, sliceArgs(arguments, 1));
4890
    } finally {
4891
      outstandingRequestCount--;
4892
      if (outstandingRequestCount === 0) {
4893
        while (outstandingRequestCallbacks.length) {
4894
          try {
4895
            outstandingRequestCallbacks.pop()();
4896
          } catch (e) {
4897
            $log.error(e);
4898
          }
4899
        }
4900
      }
4901
    }
4902
  }
4903
 
4904
  function getHash(url) {
4905
    var index = url.indexOf('#');
4906
    return index === -1 ? '' : url.substr(index + 1);
4907
  }
4908
 
4909
  /**
4910
   * @private
4911
   * Note: this method is used only by scenario runner
4912
   * TODO(vojta): prefix this method with $$ ?
4913
   * @param {function()} callback Function that will be called when no outstanding request
4914
   */
4915
  self.notifyWhenNoOutstandingRequests = function(callback) {
4916
    // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4917
    // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4918
    // regular poller would result in flaky tests.
4919
    forEach(pollFns, function(pollFn) { pollFn(); });
4920
 
4921
    if (outstandingRequestCount === 0) {
4922
      callback();
4923
    } else {
4924
      outstandingRequestCallbacks.push(callback);
4925
    }
4926
  };
4927
 
4928
  //////////////////////////////////////////////////////////////
4929
  // Poll Watcher API
4930
  //////////////////////////////////////////////////////////////
4931
  var pollFns = [],
4932
      pollTimeout;
4933
 
4934
  /**
4935
   * @name $browser#addPollFn
4936
   *
4937
   * @param {function()} fn Poll function to add
4938
   *
4939
   * @description
4940
   * Adds a function to the list of functions that poller periodically executes,
4941
   * and starts polling if not started yet.
4942
   *
4943
   * @returns {function()} the added function
4944
   */
4945
  self.addPollFn = function(fn) {
4946
    if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
4947
    pollFns.push(fn);
4948
    return fn;
4949
  };
4950
 
4951
  /**
4952
   * @param {number} interval How often should browser call poll functions (ms)
4953
   * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
4954
   *
4955
   * @description
4956
   * Configures the poller to run in the specified intervals, using the specified
4957
   * setTimeout fn and kicks it off.
4958
   */
4959
  function startPoller(interval, setTimeout) {
4960
    (function check() {
4961
      forEach(pollFns, function(pollFn) { pollFn(); });
4962
      pollTimeout = setTimeout(check, interval);
4963
    })();
4964
  }
4965
 
4966
  //////////////////////////////////////////////////////////////
4967
  // URL API
4968
  //////////////////////////////////////////////////////////////
4969
 
4970
  var cachedState, lastHistoryState,
4971
      lastBrowserUrl = location.href,
4972
      baseElement = document.find('base'),
4973
      reloadLocation = null;
4974
 
4975
  cacheState();
4976
  lastHistoryState = cachedState;
4977
 
4978
  /**
4979
   * @name $browser#url
4980
   *
4981
   * @description
4982
   * GETTER:
4983
   * Without any argument, this method just returns current value of location.href.
4984
   *
4985
   * SETTER:
4986
   * With at least one argument, this method sets url to new value.
4987
   * If html5 history api supported, pushState/replaceState is used, otherwise
4988
   * location.href/location.replace is used.
4989
   * Returns its own instance to allow chaining
4990
   *
4991
   * NOTE: this api is intended for use only by the $location service. Please use the
4992
   * {@link ng.$location $location service} to change url.
4993
   *
4994
   * @param {string} url New url (when used as setter)
4995
   * @param {boolean=} replace Should new url replace current history record?
4996
   * @param {object=} state object to use with pushState/replaceState
4997
   */
4998
  self.url = function(url, replace, state) {
4999
    // In modern browsers `history.state` is `null` by default; treating it separately
5000
    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5001
    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5002
    if (isUndefined(state)) {
5003
      state = null;
5004
    }
5005
 
5006
    // Android Browser BFCache causes location, history reference to become stale.
5007
    if (location !== window.location) location = window.location;
5008
    if (history !== window.history) history = window.history;
5009
 
5010
    // setter
5011
    if (url) {
5012
      var sameState = lastHistoryState === state;
5013
 
5014
      // Don't change anything if previous and current URLs and states match. This also prevents
5015
      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5016
      // See https://github.com/angular/angular.js/commit/ffb2701
5017
      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5018
        return self;
5019
      }
5020
      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5021
      lastBrowserUrl = url;
5022
      lastHistoryState = state;
5023
      // Don't use history API if only the hash changed
5024
      // due to a bug in IE10/IE11 which leads
5025
      // to not firing a `hashchange` nor `popstate` event
5026
      // in some cases (see #9143).
5027
      if ($sniffer.history && (!sameBase || !sameState)) {
5028
        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5029
        cacheState();
5030
        // Do the assignment again so that those two variables are referentially identical.
5031
        lastHistoryState = cachedState;
5032
      } else {
5033
        if (!sameBase) {
5034
          reloadLocation = url;
5035
        }
5036
        if (replace) {
5037
          location.replace(url);
5038
        } else if (!sameBase) {
5039
          location.href = url;
5040
        } else {
5041
          location.hash = getHash(url);
5042
        }
5043
      }
5044
      return self;
5045
    // getter
5046
    } else {
5047
      // - reloadLocation is needed as browsers don't allow to read out
5048
      //   the new location.href if a reload happened.
5049
      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5050
      return reloadLocation || location.href.replace(/%27/g,"'");
5051
    }
5052
  };
5053
 
5054
  /**
5055
   * @name $browser#state
5056
   *
5057
   * @description
5058
   * This method is a getter.
5059
   *
5060
   * Return history.state or null if history.state is undefined.
5061
   *
5062
   * @returns {object} state
5063
   */
5064
  self.state = function() {
5065
    return cachedState;
5066
  };
5067
 
5068
  var urlChangeListeners = [],
5069
      urlChangeInit = false;
5070
 
5071
  function cacheStateAndFireUrlChange() {
5072
    cacheState();
5073
    fireUrlChange();
5074
  }
5075
 
5076
  // This variable should be used *only* inside the cacheState function.
5077
  var lastCachedState = null;
5078
  function cacheState() {
5079
    // This should be the only place in $browser where `history.state` is read.
5080
    cachedState = window.history.state;
5081
    cachedState = isUndefined(cachedState) ? null : cachedState;
5082
 
5083
    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5084
    if (equals(cachedState, lastCachedState)) {
5085
      cachedState = lastCachedState;
5086
    }
5087
    lastCachedState = cachedState;
5088
  }
5089
 
5090
  function fireUrlChange() {
5091
    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5092
      return;
5093
    }
5094
 
5095
    lastBrowserUrl = self.url();
5096
    lastHistoryState = cachedState;
5097
    forEach(urlChangeListeners, function(listener) {
5098
      listener(self.url(), cachedState);
5099
    });
5100
  }
5101
 
5102
  /**
5103
   * @name $browser#onUrlChange
5104
   *
5105
   * @description
5106
   * Register callback function that will be called, when url changes.
5107
   *
5108
   * It's only called when the url is changed from outside of angular:
5109
   * - user types different url into address bar
5110
   * - user clicks on history (forward/back) button
5111
   * - user clicks on a link
5112
   *
5113
   * It's not called when url is changed by $browser.url() method
5114
   *
5115
   * The listener gets called with new url as parameter.
5116
   *
5117
   * NOTE: this api is intended for use only by the $location service. Please use the
5118
   * {@link ng.$location $location service} to monitor url changes in angular apps.
5119
   *
5120
   * @param {function(string)} listener Listener function to be called when url changes.
5121
   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5122
   */
5123
  self.onUrlChange = function(callback) {
5124
    // TODO(vojta): refactor to use node's syntax for events
5125
    if (!urlChangeInit) {
5126
      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5127
      // don't fire popstate when user change the address bar and don't fire hashchange when url
5128
      // changed by push/replaceState
5129
 
5130
      // html5 history api - popstate event
5131
      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5132
      // hashchange event
5133
      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5134
 
5135
      urlChangeInit = true;
5136
    }
5137
 
5138
    urlChangeListeners.push(callback);
5139
    return callback;
5140
  };
5141
 
5142
  /**
5143
   * Checks whether the url has changed outside of Angular.
5144
   * Needs to be exported to be able to check for changes that have been done in sync,
5145
   * as hashchange/popstate events fire in async.
5146
   */
5147
  self.$$checkUrlChange = fireUrlChange;
5148
 
5149
  //////////////////////////////////////////////////////////////
5150
  // Misc API
5151
  //////////////////////////////////////////////////////////////
5152
 
5153
  /**
5154
   * @name $browser#baseHref
5155
   *
5156
   * @description
5157
   * Returns current <base href>
5158
   * (always relative - without domain)
5159
   *
5160
   * @returns {string} The current base href
5161
   */
5162
  self.baseHref = function() {
5163
    var href = baseElement.attr('href');
5164
    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5165
  };
5166
 
5167
  //////////////////////////////////////////////////////////////
5168
  // Cookies API
5169
  //////////////////////////////////////////////////////////////
5170
  var lastCookies = {};
5171
  var lastCookieString = '';
5172
  var cookiePath = self.baseHref();
5173
 
5174
  function safeDecodeURIComponent(str) {
5175
    try {
5176
      return decodeURIComponent(str);
5177
    } catch (e) {
5178
      return str;
5179
    }
5180
  }
5181
 
5182
  /**
5183
   * @name $browser#cookies
5184
   *
5185
   * @param {string=} name Cookie name
5186
   * @param {string=} value Cookie value
5187
   *
5188
   * @description
5189
   * The cookies method provides a 'private' low level access to browser cookies.
5190
   * It is not meant to be used directly, use the $cookie service instead.
5191
   *
5192
   * The return values vary depending on the arguments that the method was called with as follows:
5193
   *
5194
   * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
5195
   *   it
5196
   * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
5197
   * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
5198
   *   way)
5199
   *
5200
   * @returns {Object} Hash of all cookies (if called without any parameter)
5201
   */
5202
  self.cookies = function(name, value) {
5203
    var cookieLength, cookieArray, cookie, i, index;
5204
 
5205
    if (name) {
5206
      if (value === undefined) {
5207
        rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
5208
                                ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
5209
      } else {
5210
        if (isString(value)) {
5211
          cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
5212
                                ';path=' + cookiePath).length + 1;
5213
 
5214
          // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
5215
          // - 300 cookies
5216
          // - 20 cookies per unique domain
5217
          // - 4096 bytes per cookie
5218
          if (cookieLength > 4096) {
5219
            $log.warn("Cookie '" + name +
5220
              "' possibly not set or overflowed because it was too large (" +
5221
              cookieLength + " > 4096 bytes)!");
5222
          }
5223
        }
5224
      }
5225
    } else {
5226
      if (rawDocument.cookie !== lastCookieString) {
5227
        lastCookieString = rawDocument.cookie;
5228
        cookieArray = lastCookieString.split("; ");
5229
        lastCookies = {};
5230
 
5231
        for (i = 0; i < cookieArray.length; i++) {
5232
          cookie = cookieArray[i];
5233
          index = cookie.indexOf('=');
5234
          if (index > 0) { //ignore nameless cookies
5235
            name = safeDecodeURIComponent(cookie.substring(0, index));
5236
            // the first value that is seen for a cookie is the most
5237
            // specific one.  values for the same cookie name that
5238
            // follow are for less specific paths.
5239
            if (lastCookies[name] === undefined) {
5240
              lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
5241
            }
5242
          }
5243
        }
5244
      }
5245
      return lastCookies;
5246
    }
5247
  };
5248
 
5249
 
5250
  /**
5251
   * @name $browser#defer
5252
   * @param {function()} fn A function, who's execution should be deferred.
5253
   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5254
   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5255
   *
5256
   * @description
5257
   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5258
   *
5259
   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5260
   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5261
   * via `$browser.defer.flush()`.
5262
   *
5263
   */
5264
  self.defer = function(fn, delay) {
5265
    var timeoutId;
5266
    outstandingRequestCount++;
5267
    timeoutId = setTimeout(function() {
5268
      delete pendingDeferIds[timeoutId];
5269
      completeOutstandingRequest(fn);
5270
    }, delay || 0);
5271
    pendingDeferIds[timeoutId] = true;
5272
    return timeoutId;
5273
  };
5274
 
5275
 
5276
  /**
5277
   * @name $browser#defer.cancel
5278
   *
5279
   * @description
5280
   * Cancels a deferred task identified with `deferId`.
5281
   *
5282
   * @param {*} deferId Token returned by the `$browser.defer` function.
5283
   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5284
   *                    canceled.
5285
   */
5286
  self.defer.cancel = function(deferId) {
5287
    if (pendingDeferIds[deferId]) {
5288
      delete pendingDeferIds[deferId];
5289
      clearTimeout(deferId);
5290
      completeOutstandingRequest(noop);
5291
      return true;
5292
    }
5293
    return false;
5294
  };
5295
 
5296
}
5297
 
5298
function $BrowserProvider() {
5299
  this.$get = ['$window', '$log', '$sniffer', '$document',
5300
      function($window, $log, $sniffer, $document) {
5301
        return new Browser($window, $document, $log, $sniffer);
5302
      }];
5303
}
5304
 
5305
/**
5306
 * @ngdoc service
5307
 * @name $cacheFactory
5308
 *
5309
 * @description
5310
 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5311
 * them.
5312
 *
5313
 * ```js
5314
 *
5315
 *  var cache = $cacheFactory('cacheId');
5316
 *  expect($cacheFactory.get('cacheId')).toBe(cache);
5317
 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5318
 *
5319
 *  cache.put("key", "value");
5320
 *  cache.put("another key", "another value");
5321
 *
5322
 *  // We've specified no options on creation
5323
 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5324
 *
5325
 * ```
5326
 *
5327
 *
5328
 * @param {string} cacheId Name or id of the newly created cache.
5329
 * @param {object=} options Options object that specifies the cache behavior. Properties:
5330
 *
5331
 *   - `{number=}` `capacity` — turns the cache into LRU cache.
5332
 *
5333
 * @returns {object} Newly created cache object with the following set of methods:
5334
 *
5335
 * - `{object}` `info()` — Returns id, size, and options of cache.
5336
 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5337
 *   it.
5338
 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5339
 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5340
 * - `{void}` `removeAll()` — Removes all cached values.
5341
 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5342
 *
5343
 * @example
5344
   <example module="cacheExampleApp">
5345
     <file name="index.html">
5346
       <div ng-controller="CacheController">
5347
         <input ng-model="newCacheKey" placeholder="Key">
5348
         <input ng-model="newCacheValue" placeholder="Value">
5349
         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5350
 
5351
         <p ng-if="keys.length">Cached Values</p>
5352
         <div ng-repeat="key in keys">
5353
           <span ng-bind="key"></span>
5354
           <span>: </span>
5355
           <b ng-bind="cache.get(key)"></b>
5356
         </div>
5357
 
5358
         <p>Cache Info</p>
5359
         <div ng-repeat="(key, value) in cache.info()">
5360
           <span ng-bind="key"></span>
5361
           <span>: </span>
5362
           <b ng-bind="value"></b>
5363
         </div>
5364
       </div>
5365
     </file>
5366
     <file name="script.js">
5367
       angular.module('cacheExampleApp', []).
5368
         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5369
           $scope.keys = [];
5370
           $scope.cache = $cacheFactory('cacheId');
5371
           $scope.put = function(key, value) {
5372
             if ($scope.cache.get(key) === undefined) {
5373
               $scope.keys.push(key);
5374
             }
5375
             $scope.cache.put(key, value === undefined ? null : value);
5376
           };
5377
         }]);
5378
     </file>
5379
     <file name="style.css">
5380
       p {
5381
         margin: 10px 0 3px;
5382
       }
5383
     </file>
5384
   </example>
5385
 */
5386
function $CacheFactoryProvider() {
5387
 
5388
  this.$get = function() {
5389
    var caches = {};
5390
 
5391
    function cacheFactory(cacheId, options) {
5392
      if (cacheId in caches) {
5393
        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5394
      }
5395
 
5396
      var size = 0,
5397
          stats = extend({}, options, {id: cacheId}),
5398
          data = {},
5399
          capacity = (options && options.capacity) || Number.MAX_VALUE,
5400
          lruHash = {},
5401
          freshEnd = null,
5402
          staleEnd = null;
5403
 
5404
      /**
5405
       * @ngdoc type
5406
       * @name $cacheFactory.Cache
5407
       *
5408
       * @description
5409
       * A cache object used to store and retrieve data, primarily used by
5410
       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5411
       * templates and other data.
5412
       *
5413
       * ```js
5414
       *  angular.module('superCache')
5415
       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5416
       *      return $cacheFactory('super-cache');
5417
       *    }]);
5418
       * ```
5419
       *
5420
       * Example test:
5421
       *
5422
       * ```js
5423
       *  it('should behave like a cache', inject(function(superCache) {
5424
       *    superCache.put('key', 'value');
5425
       *    superCache.put('another key', 'another value');
5426
       *
5427
       *    expect(superCache.info()).toEqual({
5428
       *      id: 'super-cache',
5429
       *      size: 2
5430
       *    });
5431
       *
5432
       *    superCache.remove('another key');
5433
       *    expect(superCache.get('another key')).toBeUndefined();
5434
       *
5435
       *    superCache.removeAll();
5436
       *    expect(superCache.info()).toEqual({
5437
       *      id: 'super-cache',
5438
       *      size: 0
5439
       *    });
5440
       *  }));
5441
       * ```
5442
       */
5443
      return caches[cacheId] = {
5444
 
5445
        /**
5446
         * @ngdoc method
5447
         * @name $cacheFactory.Cache#put
5448
         * @kind function
5449
         *
5450
         * @description
5451
         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5452
         * retrieved later, and incrementing the size of the cache if the key was not already
5453
         * present in the cache. If behaving like an LRU cache, it will also remove stale
5454
         * entries from the set.
5455
         *
5456
         * It will not insert undefined values into the cache.
5457
         *
5458
         * @param {string} key the key under which the cached data is stored.
5459
         * @param {*} value the value to store alongside the key. If it is undefined, the key
5460
         *    will not be stored.
5461
         * @returns {*} the value stored.
5462
         */
5463
        put: function(key, value) {
5464
          if (capacity < Number.MAX_VALUE) {
5465
            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5466
 
5467
            refresh(lruEntry);
5468
          }
5469
 
5470
          if (isUndefined(value)) return;
5471
          if (!(key in data)) size++;
5472
          data[key] = value;
5473
 
5474
          if (size > capacity) {
5475
            this.remove(staleEnd.key);
5476
          }
5477
 
5478
          return value;
5479
        },
5480
 
5481
        /**
5482
         * @ngdoc method
5483
         * @name $cacheFactory.Cache#get
5484
         * @kind function
5485
         *
5486
         * @description
5487
         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5488
         *
5489
         * @param {string} key the key of the data to be retrieved
5490
         * @returns {*} the value stored.
5491
         */
5492
        get: function(key) {
5493
          if (capacity < Number.MAX_VALUE) {
5494
            var lruEntry = lruHash[key];
5495
 
5496
            if (!lruEntry) return;
5497
 
5498
            refresh(lruEntry);
5499
          }
5500
 
5501
          return data[key];
5502
        },
5503
 
5504
 
5505
        /**
5506
         * @ngdoc method
5507
         * @name $cacheFactory.Cache#remove
5508
         * @kind function
5509
         *
5510
         * @description
5511
         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5512
         *
5513
         * @param {string} key the key of the entry to be removed
5514
         */
5515
        remove: function(key) {
5516
          if (capacity < Number.MAX_VALUE) {
5517
            var lruEntry = lruHash[key];
5518
 
5519
            if (!lruEntry) return;
5520
 
5521
            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5522
            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5523
            link(lruEntry.n,lruEntry.p);
5524
 
5525
            delete lruHash[key];
5526
          }
5527
 
5528
          delete data[key];
5529
          size--;
5530
        },
5531
 
5532
 
5533
        /**
5534
         * @ngdoc method
5535
         * @name $cacheFactory.Cache#removeAll
5536
         * @kind function
5537
         *
5538
         * @description
5539
         * Clears the cache object of any entries.
5540
         */
5541
        removeAll: function() {
5542
          data = {};
5543
          size = 0;
5544
          lruHash = {};
5545
          freshEnd = staleEnd = null;
5546
        },
5547
 
5548
 
5549
        /**
5550
         * @ngdoc method
5551
         * @name $cacheFactory.Cache#destroy
5552
         * @kind function
5553
         *
5554
         * @description
5555
         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5556
         * removing it from the {@link $cacheFactory $cacheFactory} set.
5557
         */
5558
        destroy: function() {
5559
          data = null;
5560
          stats = null;
5561
          lruHash = null;
5562
          delete caches[cacheId];
5563
        },
5564
 
5565
 
5566
        /**
5567
         * @ngdoc method
5568
         * @name $cacheFactory.Cache#info
5569
         * @kind function
5570
         *
5571
         * @description
5572
         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5573
         *
5574
         * @returns {object} an object with the following properties:
5575
         *   <ul>
5576
         *     <li>**id**: the id of the cache instance</li>
5577
         *     <li>**size**: the number of entries kept in the cache instance</li>
5578
         *     <li>**...**: any additional properties from the options object when creating the
5579
         *       cache.</li>
5580
         *   </ul>
5581
         */
5582
        info: function() {
5583
          return extend({}, stats, {size: size});
5584
        }
5585
      };
5586
 
5587
 
5588
      /**
5589
       * makes the `entry` the freshEnd of the LRU linked list
5590
       */
5591
      function refresh(entry) {
5592
        if (entry != freshEnd) {
5593
          if (!staleEnd) {
5594
            staleEnd = entry;
5595
          } else if (staleEnd == entry) {
5596
            staleEnd = entry.n;
5597
          }
5598
 
5599
          link(entry.n, entry.p);
5600
          link(entry, freshEnd);
5601
          freshEnd = entry;
5602
          freshEnd.n = null;
5603
        }
5604
      }
5605
 
5606
 
5607
      /**
5608
       * bidirectionally links two entries of the LRU linked list
5609
       */
5610
      function link(nextEntry, prevEntry) {
5611
        if (nextEntry != prevEntry) {
5612
          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5613
          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5614
        }
5615
      }
5616
    }
5617
 
5618
 
5619
  /**
5620
   * @ngdoc method
5621
   * @name $cacheFactory#info
5622
   *
5623
   * @description
5624
   * Get information about all the caches that have been created
5625
   *
5626
   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5627
   */
5628
    cacheFactory.info = function() {
5629
      var info = {};
5630
      forEach(caches, function(cache, cacheId) {
5631
        info[cacheId] = cache.info();
5632
      });
5633
      return info;
5634
    };
5635
 
5636
 
5637
  /**
5638
   * @ngdoc method
5639
   * @name $cacheFactory#get
5640
   *
5641
   * @description
5642
   * Get access to a cache object by the `cacheId` used when it was created.
5643
   *
5644
   * @param {string} cacheId Name or id of a cache to access.
5645
   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5646
   */
5647
    cacheFactory.get = function(cacheId) {
5648
      return caches[cacheId];
5649
    };
5650
 
5651
 
5652
    return cacheFactory;
5653
  };
5654
}
5655
 
5656
/**
5657
 * @ngdoc service
5658
 * @name $templateCache
5659
 *
5660
 * @description
5661
 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5662
 * can load templates directly into the cache in a `script` tag, or by consuming the
5663
 * `$templateCache` service directly.
5664
 *
5665
 * Adding via the `script` tag:
5666
 *
5667
 * ```html
5668
 *   <script type="text/ng-template" id="templateId.html">
5669
 *     <p>This is the content of the template</p>
5670
 *   </script>
5671
 * ```
5672
 *
5673
 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5674
 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
5675
 * element with ng-app attribute), otherwise the template will be ignored.
5676
 *
5677
 * Adding via the $templateCache service:
5678
 *
5679
 * ```js
5680
 * var myApp = angular.module('myApp', []);
5681
 * myApp.run(function($templateCache) {
5682
 *   $templateCache.put('templateId.html', 'This is the content of the template');
5683
 * });
5684
 * ```
5685
 *
5686
 * To retrieve the template later, simply use it in your HTML:
5687
 * ```html
5688
 * <div ng-include=" 'templateId.html' "></div>
5689
 * ```
5690
 *
5691
 * or get it via Javascript:
5692
 * ```js
5693
 * $templateCache.get('templateId.html')
5694
 * ```
5695
 *
5696
 * See {@link ng.$cacheFactory $cacheFactory}.
5697
 *
5698
 */
5699
function $TemplateCacheProvider() {
5700
  this.$get = ['$cacheFactory', function($cacheFactory) {
5701
    return $cacheFactory('templates');
5702
  }];
5703
}
5704
 
5705
/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5706
 *
5707
 * DOM-related variables:
5708
 *
5709
 * - "node" - DOM Node
5710
 * - "element" - DOM Element or Node
5711
 * - "$node" or "$element" - jqLite-wrapped node or element
5712
 *
5713
 *
5714
 * Compiler related stuff:
5715
 *
5716
 * - "linkFn" - linking fn of a single directive
5717
 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5718
 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
5719
 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5720
 */
5721
 
5722
 
5723
/**
5724
 * @ngdoc service
5725
 * @name $compile
5726
 * @kind function
5727
 *
5728
 * @description
5729
 * Compiles an HTML string or DOM into a template and produces a template function, which
5730
 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5731
 *
5732
 * The compilation is a process of walking the DOM tree and matching DOM elements to
5733
 * {@link ng.$compileProvider#directive directives}.
5734
 *
5735
 * <div class="alert alert-warning">
5736
 * **Note:** This document is an in-depth reference of all directive options.
5737
 * For a gentle introduction to directives with examples of common use cases,
5738
 * see the {@link guide/directive directive guide}.
5739
 * </div>
5740
 *
5741
 * ## Comprehensive Directive API
5742
 *
5743
 * There are many different options for a directive.
5744
 *
5745
 * The difference resides in the return value of the factory function.
5746
 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5747
 * or just the `postLink` function (all other properties will have the default values).
5748
 *
5749
 * <div class="alert alert-success">
5750
 * **Best Practice:** It's recommended to use the "directive definition object" form.
5751
 * </div>
5752
 *
5753
 * Here's an example directive declared with a Directive Definition Object:
5754
 *
5755
 * ```js
5756
 *   var myModule = angular.module(...);
5757
 *
5758
 *   myModule.directive('directiveName', function factory(injectables) {
5759
 *     var directiveDefinitionObject = {
5760
 *       priority: 0,
5761
 *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5762
 *       // or
5763
 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5764
 *       transclude: false,
5765
 *       restrict: 'A',
5766
 *       templateNamespace: 'html',
5767
 *       scope: false,
5768
 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5769
 *       controllerAs: 'stringAlias',
5770
 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5771
 *       compile: function compile(tElement, tAttrs, transclude) {
5772
 *         return {
5773
 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5774
 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
5775
 *         }
5776
 *         // or
5777
 *         // return function postLink( ... ) { ... }
5778
 *       },
5779
 *       // or
5780
 *       // link: {
5781
 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5782
 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
5783
 *       // }
5784
 *       // or
5785
 *       // link: function postLink( ... ) { ... }
5786
 *     };
5787
 *     return directiveDefinitionObject;
5788
 *   });
5789
 * ```
5790
 *
5791
 * <div class="alert alert-warning">
5792
 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5793
 * </div>
5794
 *
5795
 * Therefore the above can be simplified as:
5796
 *
5797
 * ```js
5798
 *   var myModule = angular.module(...);
5799
 *
5800
 *   myModule.directive('directiveName', function factory(injectables) {
5801
 *     var directiveDefinitionObject = {
5802
 *       link: function postLink(scope, iElement, iAttrs) { ... }
5803
 *     };
5804
 *     return directiveDefinitionObject;
5805
 *     // or
5806
 *     // return function postLink(scope, iElement, iAttrs) { ... }
5807
 *   });
5808
 * ```
5809
 *
5810
 *
5811
 *
5812
 * ### Directive Definition Object
5813
 *
5814
 * The directive definition object provides instructions to the {@link ng.$compile
5815
 * compiler}. The attributes are:
5816
 *
5817
 * #### `multiElement`
5818
 * When this property is set to true, the HTML compiler will collect DOM nodes between
5819
 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5820
 * together as the directive elements. It is recommended that this feature be used on directives
5821
 * which are not strictly behavioural (such as {@link ngClick}), and which
5822
 * do not manipulate or replace child nodes (such as {@link ngInclude}).
5823
 *
5824
 * #### `priority`
5825
 * When there are multiple directives defined on a single DOM element, sometimes it
5826
 * is necessary to specify the order in which the directives are applied. The `priority` is used
5827
 * to sort the directives before their `compile` functions get called. Priority is defined as a
5828
 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5829
 * are also run in priority order, but post-link functions are run in reverse order. The order
5830
 * of directives with the same priority is undefined. The default priority is `0`.
5831
 *
5832
 * #### `terminal`
5833
 * If set to true then the current `priority` will be the last set of directives
5834
 * which will execute (any directives at the current priority will still execute
5835
 * as the order of execution on same `priority` is undefined). Note that expressions
5836
 * and other directives used in the directive's template will also be excluded from execution.
5837
 *
5838
 * #### `scope`
5839
 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5840
 * same element request a new scope, only one new scope is created. The new scope rule does not
5841
 * apply for the root of the template since the root of the template always gets a new scope.
5842
 *
5843
 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5844
 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5845
 * when creating reusable components, which should not accidentally read or modify data in the
5846
 * parent scope.
5847
 *
5848
 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5849
 * derived from the parent scope. These local properties are useful for aliasing values for
5850
 * templates. Locals definition is a hash of local scope property to its source:
5851
 *
5852
 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5853
 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
5854
 *   attribute name is assumed to be the same as the local name.
5855
 *   Given `<widget my-attr="hello {{name}}">` and widget definition
5856
 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5857
 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5858
 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
5859
 *   component scope).
5860
 *
5861
 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5862
 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5863
 *   name is specified then the attribute name is assumed to be the same as the local name.
5864
 *   Given `<widget my-attr="parentModel">` and widget definition of
5865
 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5866
 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5867
 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5868
 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5869
 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
5870
 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
5871
 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
5872
 *
5873
 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5874
 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
5875
 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
5876
 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5877
 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
5878
 *   pass data from the isolated scope via an expression to the parent scope, this can be
5879
 *   done by passing a map of local variable names and values into the expression wrapper fn.
5880
 *   For example, if the expression is `increment(amount)` then we can specify the amount value
5881
 *   by calling the `localFn` as `localFn({amount: 22})`.
5882
 *
5883
 *
5884
 * #### `bindToController`
5885
 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5886
 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5887
 * is instantiated, the initial values of the isolate scope bindings are already available.
5888
 *
5889
 * #### `controller`
5890
 * Controller constructor function. The controller is instantiated before the
5891
 * pre-linking phase and it is shared with other directives (see
5892
 * `require` attribute). This allows the directives to communicate with each other and augment
5893
 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5894
 *
5895
 * * `$scope` - Current scope associated with the element
5896
 * * `$element` - Current element
5897
 * * `$attrs` - Current attributes object for the element
5898
 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
5899
 *   `function([scope], cloneLinkingFn, futureParentElement)`.
5900
 *    * `scope`: optional argument to override the scope.
5901
 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
5902
 *    * `futureParentElement`:
5903
 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
5904
 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
5905
 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
5906
 *          and when the `cloneLinkinFn` is passed,
5907
 *          as those elements need to created and cloned in a special way when they are defined outside their
5908
 *          usual containers (e.g. like `<svg>`).
5909
 *        * See also the `directive.templateNamespace` property.
5910
 *
5911
 *
5912
 * #### `require`
5913
 * Require another directive and inject its controller as the fourth argument to the linking function. The
5914
 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5915
 * injected argument will be an array in corresponding order. If no such directive can be
5916
 * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
5917
 *
5918
 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5919
 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5920
 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5921
 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
5922
 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5923
 *   `null` to the `link` fn if not found.
5924
 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
5925
 *   `null` to the `link` fn if not found.
5926
 *
5927
 *
5928
 * #### `controllerAs`
5929
 * Controller alias at the directive scope. An alias for the controller so it
5930
 * can be referenced at the directive template. The directive needs to define a scope for this
5931
 * configuration to be used. Useful in the case when directive is used as component.
5932
 *
5933
 *
5934
 * #### `restrict`
5935
 * String of subset of `EACM` which restricts the directive to a specific directive
5936
 * declaration style. If omitted, the defaults (elements and attributes) are used.
5937
 *
5938
 * * `E` - Element name (default): `<my-directive></my-directive>`
5939
 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5940
 * * `C` - Class: `<div class="my-directive: exp;"></div>`
5941
 * * `M` - Comment: `<!-- directive: my-directive exp -->`
5942
 *
5943
 *
5944
 * #### `templateNamespace`
5945
 * String representing the document type used by the markup in the template.
5946
 * AngularJS needs this information as those elements need to be created and cloned
5947
 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
5948
 *
5949
 * * `html` - All root nodes in the template are HTML. Root nodes may also be
5950
 *   top-level elements such as `<svg>` or `<math>`.
5951
 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
5952
 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
5953
 *
5954
 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
5955
 *
5956
 * #### `template`
5957
 * HTML markup that may:
5958
 * * Replace the contents of the directive's element (default).
5959
 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5960
 * * Wrap the contents of the directive's element (if `transclude` is true).
5961
 *
5962
 * Value may be:
5963
 *
5964
 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5965
 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5966
 *   function api below) and returns a string value.
5967
 *
5968
 *
5969
 * #### `templateUrl`
5970
 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
5971
 *
5972
 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
5973
 * for later when the template has been resolved.  In the meantime it will continue to compile and link
5974
 * sibling and parent elements as though this element had not contained any directives.
5975
 *
5976
 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
5977
 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
5978
 * case when only one deeply nested directive has `templateUrl`.
5979
 *
5980
 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
5981
 *
5982
 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
5983
 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
5984
 * a string value representing the url.  In either case, the template URL is passed through {@link
5985
 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
5986
 *
5987
 *
5988
 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
5989
 * specify what the template should replace. Defaults to `false`.
5990
 *
5991
 * * `true` - the template will replace the directive's element.
5992
 * * `false` - the template will replace the contents of the directive's element.
5993
 *
5994
 * The replacement process migrates all of the attributes / classes from the old element to the new
5995
 * one. See the {@link guide/directive#template-expanding-directive
5996
 * Directives Guide} for an example.
5997
 *
5998
 * There are very few scenarios where element replacement is required for the application function,
5999
 * the main one being reusable custom components that are used within SVG contexts
6000
 * (because SVG doesn't work with custom elements in the DOM tree).
6001
 *
6002
 * #### `transclude`
6003
 * Extract the contents of the element where the directive appears and make it available to the directive.
6004
 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6005
 * {@link $compile#transclusion Transclusion} section below.
6006
 *
6007
 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6008
 * directive's element or the entire element:
6009
 *
6010
 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6011
 * * `'element'` - transclude the whole of the directive's element including any directives on this
6012
 *   element that defined at a lower priority than this directive. When used, the `template`
6013
 *   property is ignored.
6014
 *
6015
 *
6016
 * #### `compile`
6017
 *
6018
 * ```js
6019
 *   function compile(tElement, tAttrs, transclude) { ... }
6020
 * ```
6021
 *
6022
 * The compile function deals with transforming the template DOM. Since most directives do not do
6023
 * template transformation, it is not used often. The compile function takes the following arguments:
6024
 *
6025
 *   * `tElement` - template element - The element where the directive has been declared. It is
6026
 *     safe to do template transformation on the element and child elements only.
6027
 *
6028
 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6029
 *     between all directive compile functions.
6030
 *
6031
 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6032
 *
6033
 * <div class="alert alert-warning">
6034
 * **Note:** The template instance and the link instance may be different objects if the template has
6035
 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6036
 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6037
 * should be done in a linking function rather than in a compile function.
6038
 * </div>
6039
 
6040
 * <div class="alert alert-warning">
6041
 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6042
 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6043
 * stack overflow errors.
6044
 *
6045
 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6046
 * a directive's template instead of relying on automatic template compilation via `template` or
6047
 * `templateUrl` declaration or manual compilation inside the compile function.
6048
 * </div>
6049
 *
6050
 * <div class="alert alert-error">
6051
 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6052
 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6053
 *   to the link function instead.
6054
 * </div>
6055
 
6056
 * A compile function can have a return value which can be either a function or an object.
6057
 *
6058
 * * returning a (post-link) function - is equivalent to registering the linking function via the
6059
 *   `link` property of the config object when the compile function is empty.
6060
 *
6061
 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6062
 *   control when a linking function should be called during the linking phase. See info about
6063
 *   pre-linking and post-linking functions below.
6064
 *
6065
 *
6066
 * #### `link`
6067
 * This property is used only if the `compile` property is not defined.
6068
 *
6069
 * ```js
6070
 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6071
 * ```
6072
 *
6073
 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6074
 * executed after the template has been cloned. This is where most of the directive logic will be
6075
 * put.
6076
 *
6077
 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6078
 *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6079
 *
6080
 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6081
 *     manipulate the children of the element only in `postLink` function since the children have
6082
 *     already been linked.
6083
 *
6084
 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6085
 *     between all directive linking functions.
6086
 *
6087
 *   * `controller` - a controller instance - A controller instance if at least one directive on the
6088
 *     element defines a controller. The controller is shared among all the directives, which allows
6089
 *     the directives to use the controllers as a communication channel.
6090
 *
6091
 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6092
 *     This is the same as the `$transclude`
6093
 *     parameter of directive controllers, see there for details.
6094
 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6095
 *
6096
 * #### Pre-linking function
6097
 *
6098
 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6099
 * compiler linking function will fail to locate the correct elements for linking.
6100
 *
6101
 * #### Post-linking function
6102
 *
6103
 * Executed after the child elements are linked.
6104
 *
6105
 * Note that child elements that contain `templateUrl` directives will not have been compiled
6106
 * and linked since they are waiting for their template to load asynchronously and their own
6107
 * compilation and linking has been suspended until that occurs.
6108
 *
6109
 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6110
 * for their async templates to be resolved.
6111
 *
6112
 *
6113
 * ### Transclusion
6114
 *
6115
 * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
6116
 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6117
 * scope from where they were taken.
6118
 *
6119
 * Transclusion is used (often with {@link ngTransclude}) to insert the
6120
 * original contents of a directive's element into a specified place in the template of the directive.
6121
 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6122
 * content has access to the properties on the scope from which it was taken, even if the directive
6123
 * has isolated scope.
6124
 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6125
 *
6126
 * This makes it possible for the widget to have private state for its template, while the transcluded
6127
 * content has access to its originating scope.
6128
 *
6129
 * <div class="alert alert-warning">
6130
 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6131
 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6132
 * Testing Transclusion Directives}.
6133
 * </div>
6134
 *
6135
 * #### Transclusion Functions
6136
 *
6137
 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6138
 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6139
 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6140
 *
6141
 * <div class="alert alert-info">
6142
 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6143
 * ngTransclude will deal with it for us.
6144
 * </div>
6145
 *
6146
 * If you want to manually control the insertion and removal of the transcluded content in your directive
6147
 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6148
 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6149
 *
6150
 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6151
 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6152
 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6153
 *
6154
 * <div class="alert alert-info">
6155
 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6156
 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6157
 * </div>
6158
 *
6159
 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6160
 * attach function**:
6161
 *
6162
 * ```js
6163
 * var transcludedContent, transclusionScope;
6164
 *
6165
 * $transclude(function(clone, scope) {
6166
 *   element.append(clone);
6167
 *   transcludedContent = clone;
6168
 *   transclusionScope = scope;
6169
 * });
6170
 * ```
6171
 *
6172
 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6173
 * associated transclusion scope:
6174
 *
6175
 * ```js
6176
 * transcludedContent.remove();
6177
 * transclusionScope.$destroy();
6178
 * ```
6179
 *
6180
 * <div class="alert alert-info">
6181
 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6182
 * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
6183
 * then you are also responsible for calling `$destroy` on the transclusion scope.
6184
 * </div>
6185
 *
6186
 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6187
 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6188
 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6189
 *
6190
 *
6191
 * #### Transclusion Scopes
6192
 *
6193
 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6194
 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6195
 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6196
 * was taken.
6197
 *
6198
 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6199
 * like this:
6200
 *
6201
 * ```html
6202
 * <div ng-app>
6203
 *   <div isolate>
6204
 *     <div transclusion>
6205
 *     </div>
6206
 *   </div>
6207
 * </div>
6208
 * ```
6209
 *
6210
 * The `$parent` scope hierarchy will look like this:
6211
 *
6212
 * ```
6213
 * - $rootScope
6214
 *   - isolate
6215
 *     - transclusion
6216
 * ```
6217
 *
6218
 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6219
 *
6220
 * ```
6221
 * - $rootScope
6222
 *   - transclusion
6223
 * - isolate
6224
 * ```
6225
 *
6226
 *
6227
 * ### Attributes
6228
 *
6229
 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6230
 * `link()` or `compile()` functions. It has a variety of uses.
6231
 *
6232
 * accessing *Normalized attribute names:*
6233
 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6234
 * the attributes object allows for normalized access to
6235
 *   the attributes.
6236
 *
6237
 * * *Directive inter-communication:* All directives share the same instance of the attributes
6238
 *   object which allows the directives to use the attributes object as inter directive
6239
 *   communication.
6240
 *
6241
 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6242
 *   allowing other directives to read the interpolated value.
6243
 *
6244
 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6245
 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6246
 *   the only way to easily get the actual value because during the linking phase the interpolation
6247
 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6248
 *
6249
 * ```js
6250
 * function linkingFn(scope, elm, attrs, ctrl) {
6251
 *   // get the attribute value
6252
 *   console.log(attrs.ngModel);
6253
 *
6254
 *   // change the attribute
6255
 *   attrs.$set('ngModel', 'new value');
6256
 *
6257
 *   // observe changes to interpolated attribute
6258
 *   attrs.$observe('ngModel', function(value) {
6259
 *     console.log('ngModel has changed value to ' + value);
6260
 *   });
6261
 * }
6262
 * ```
6263
 *
6264
 * ## Example
6265
 *
6266
 * <div class="alert alert-warning">
6267
 * **Note**: Typically directives are registered with `module.directive`. The example below is
6268
 * to illustrate how `$compile` works.
6269
 * </div>
6270
 *
6271
 <example module="compileExample">
6272
   <file name="index.html">
6273
    <script>
6274
      angular.module('compileExample', [], function($compileProvider) {
6275
        // configure new 'compile' directive by passing a directive
6276
        // factory function. The factory function injects the '$compile'
6277
        $compileProvider.directive('compile', function($compile) {
6278
          // directive factory creates a link function
6279
          return function(scope, element, attrs) {
6280
            scope.$watch(
6281
              function(scope) {
6282
                 // watch the 'compile' expression for changes
6283
                return scope.$eval(attrs.compile);
6284
              },
6285
              function(value) {
6286
                // when the 'compile' expression changes
6287
                // assign it into the current DOM
6288
                element.html(value);
6289
 
6290
                // compile the new DOM and link it to the current
6291
                // scope.
6292
                // NOTE: we only compile .childNodes so that
6293
                // we don't get into infinite loop compiling ourselves
6294
                $compile(element.contents())(scope);
6295
              }
6296
            );
6297
          };
6298
        });
6299
      })
6300
      .controller('GreeterController', ['$scope', function($scope) {
6301
        $scope.name = 'Angular';
6302
        $scope.html = 'Hello {{name}}';
6303
      }]);
6304
    </script>
6305
    <div ng-controller="GreeterController">
6306
      <input ng-model="name"> <br>
6307
      <textarea ng-model="html"></textarea> <br>
6308
      <div compile="html"></div>
6309
    </div>
6310
   </file>
6311
   <file name="protractor.js" type="protractor">
6312
     it('should auto compile', function() {
6313
       var textarea = $('textarea');
6314
       var output = $('div[compile]');
6315
       // The initial state reads 'Hello Angular'.
6316
       expect(output.getText()).toBe('Hello Angular');
6317
       textarea.clear();
6318
       textarea.sendKeys('{{name}}!');
6319
       expect(output.getText()).toBe('Angular!');
6320
     });
6321
   </file>
6322
 </example>
6323
 
6324
 *
6325
 *
6326
 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6327
 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6328
 *
6329
 * <div class="alert alert-error">
6330
 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6331
 *   e.g. will not use the right outer scope. Please pass the transclude function as a
6332
 *   `parentBoundTranscludeFn` to the link function instead.
6333
 * </div>
6334
 *
6335
 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6336
 *                 root element(s), not their children)
6337
 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6338
 * (a DOM element/tree) to a scope. Where:
6339
 *
6340
 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6341
 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6342
 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
6343
 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6344
 *  called as: <br> `cloneAttachFn(clonedElement, scope)` where:
6345
 *
6346
 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
6347
 *      * `scope` - is the current scope with which the linking function is working with.
6348
 *
6349
 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
6350
 *  keys may be used to control linking behavior:
6351
 *
6352
 *      * `parentBoundTranscludeFn` - the transclude function made available to
6353
 *        directives; if given, it will be passed through to the link functions of
6354
 *        directives found in `element` during compilation.
6355
 *      * `transcludeControllers` - an object hash with keys that map controller names
6356
 *        to controller instances; if given, it will make the controllers
6357
 *        available to directives.
6358
 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6359
 *        the cloned elements; only needed for transcludes that are allowed to contain non html
6360
 *        elements (e.g. SVG elements). See also the directive.controller property.
6361
 *
6362
 * Calling the linking function returns the element of the template. It is either the original
6363
 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6364
 *
6365
 * After linking the view is not updated until after a call to $digest which typically is done by
6366
 * Angular automatically.
6367
 *
6368
 * If you need access to the bound view, there are two ways to do it:
6369
 *
6370
 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6371
 *   before you send them to the compiler and keep this reference around.
6372
 *   ```js
6373
 *     var element = $compile('<p>{{total}}</p>')(scope);
6374
 *   ```
6375
 *
6376
 * - if on the other hand, you need the element to be cloned, the view reference from the original
6377
 *   example would not point to the clone, but rather to the original template that was cloned. In
6378
 *   this case, you can access the clone via the cloneAttachFn:
6379
 *   ```js
6380
 *     var templateElement = angular.element('<p>{{total}}</p>'),
6381
 *         scope = ....;
6382
 *
6383
 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6384
 *       //attach the clone to DOM document at the right place
6385
 *     });
6386
 *
6387
 *     //now we have reference to the cloned DOM via `clonedElement`
6388
 *   ```
6389
 *
6390
 *
6391
 * For information on how the compiler works, see the
6392
 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6393
 */
6394
 
6395
var $compileMinErr = minErr('$compile');
6396
 
6397
/**
6398
 * @ngdoc provider
6399
 * @name $compileProvider
6400
 *
6401
 * @description
6402
 */
6403
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6404
function $CompileProvider($provide, $$sanitizeUriProvider) {
6405
  var hasDirectives = {},
6406
      Suffix = 'Directive',
6407
      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6408
      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6409
      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6410
      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6411
 
6412
  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6413
  // The assumption is that future DOM event attribute names will begin with
6414
  // 'on' and be composed of only English letters.
6415
  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6416
 
6417
  function parseIsolateBindings(scope, directiveName) {
6418
    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6419
 
6420
    var bindings = {};
6421
 
6422
    forEach(scope, function(definition, scopeName) {
6423
      var match = definition.match(LOCAL_REGEXP);
6424
 
6425
      if (!match) {
6426
        throw $compileMinErr('iscp',
6427
            "Invalid isolate scope definition for directive '{0}'." +
6428
            " Definition: {... {1}: '{2}' ...}",
6429
            directiveName, scopeName, definition);
6430
      }
6431
 
6432
      bindings[scopeName] = {
6433
        mode: match[1][0],
6434
        collection: match[2] === '*',
6435
        optional: match[3] === '?',
6436
        attrName: match[4] || scopeName
6437
      };
6438
    });
6439
 
6440
    return bindings;
6441
  }
6442
 
6443
  /**
6444
   * @ngdoc method
6445
   * @name $compileProvider#directive
6446
   * @kind function
6447
   *
6448
   * @description
6449
   * Register a new directive with the compiler.
6450
   *
6451
   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6452
   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6453
   *    names and the values are the factories.
6454
   * @param {Function|Array} directiveFactory An injectable directive factory function. See
6455
   *    {@link guide/directive} for more info.
6456
   * @returns {ng.$compileProvider} Self for chaining.
6457
   */
6458
   this.directive = function registerDirective(name, directiveFactory) {
6459
    assertNotHasOwnProperty(name, 'directive');
6460
    if (isString(name)) {
6461
      assertArg(directiveFactory, 'directiveFactory');
6462
      if (!hasDirectives.hasOwnProperty(name)) {
6463
        hasDirectives[name] = [];
6464
        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6465
          function($injector, $exceptionHandler) {
6466
            var directives = [];
6467
            forEach(hasDirectives[name], function(directiveFactory, index) {
6468
              try {
6469
                var directive = $injector.invoke(directiveFactory);
6470
                if (isFunction(directive)) {
6471
                  directive = { compile: valueFn(directive) };
6472
                } else if (!directive.compile && directive.link) {
6473
                  directive.compile = valueFn(directive.link);
6474
                }
6475
                directive.priority = directive.priority || 0;
6476
                directive.index = index;
6477
                directive.name = directive.name || name;
6478
                directive.require = directive.require || (directive.controller && directive.name);
6479
                directive.restrict = directive.restrict || 'EA';
6480
                if (isObject(directive.scope)) {
6481
                  directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
6482
                }
6483
                directives.push(directive);
6484
              } catch (e) {
6485
                $exceptionHandler(e);
6486
              }
6487
            });
6488
            return directives;
6489
          }]);
6490
      }
6491
      hasDirectives[name].push(directiveFactory);
6492
    } else {
6493
      forEach(name, reverseParams(registerDirective));
6494
    }
6495
    return this;
6496
  };
6497
 
6498
 
6499
  /**
6500
   * @ngdoc method
6501
   * @name $compileProvider#aHrefSanitizationWhitelist
6502
   * @kind function
6503
   *
6504
   * @description
6505
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6506
   * urls during a[href] sanitization.
6507
   *
6508
   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6509
   *
6510
   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6511
   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6512
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6513
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6514
   *
6515
   * @param {RegExp=} regexp New regexp to whitelist urls with.
6516
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6517
   *    chaining otherwise.
6518
   */
6519
  this.aHrefSanitizationWhitelist = function(regexp) {
6520
    if (isDefined(regexp)) {
6521
      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6522
      return this;
6523
    } else {
6524
      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6525
    }
6526
  };
6527
 
6528
 
6529
  /**
6530
   * @ngdoc method
6531
   * @name $compileProvider#imgSrcSanitizationWhitelist
6532
   * @kind function
6533
   *
6534
   * @description
6535
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6536
   * urls during img[src] sanitization.
6537
   *
6538
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
6539
   *
6540
   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
6541
   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
6542
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6543
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6544
   *
6545
   * @param {RegExp=} regexp New regexp to whitelist urls with.
6546
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6547
   *    chaining otherwise.
6548
   */
6549
  this.imgSrcSanitizationWhitelist = function(regexp) {
6550
    if (isDefined(regexp)) {
6551
      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
6552
      return this;
6553
    } else {
6554
      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
6555
    }
6556
  };
6557
 
6558
  /**
6559
   * @ngdoc method
6560
   * @name  $compileProvider#debugInfoEnabled
6561
   *
6562
   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
6563
   * current debugInfoEnabled state
6564
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
6565
   *
6566
   * @kind function
6567
   *
6568
   * @description
6569
   * Call this method to enable/disable various debug runtime information in the compiler such as adding
6570
   * binding information and a reference to the current scope on to DOM elements.
6571
   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
6572
   * * `ng-binding` CSS class
6573
   * * `$binding` data property containing an array of the binding expressions
6574
   *
6575
   * You may want to disable this in production for a significant performance boost. See
6576
   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
6577
   *
6578
   * The default value is true.
6579
   */
6580
  var debugInfoEnabled = true;
6581
  this.debugInfoEnabled = function(enabled) {
6582
    if (isDefined(enabled)) {
6583
      debugInfoEnabled = enabled;
6584
      return this;
6585
    }
6586
    return debugInfoEnabled;
6587
  };
6588
 
6589
  this.$get = [
6590
            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
6591
            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
6592
    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
6593
             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
6594
 
6595
    var Attributes = function(element, attributesToCopy) {
6596
      if (attributesToCopy) {
6597
        var keys = Object.keys(attributesToCopy);
6598
        var i, l, key;
6599
 
6600
        for (i = 0, l = keys.length; i < l; i++) {
6601
          key = keys[i];
6602
          this[key] = attributesToCopy[key];
6603
        }
6604
      } else {
6605
        this.$attr = {};
6606
      }
6607
 
6608
      this.$$element = element;
6609
    };
6610
 
6611
    Attributes.prototype = {
6612
      /**
6613
       * @ngdoc method
6614
       * @name $compile.directive.Attributes#$normalize
6615
       * @kind function
6616
       *
6617
       * @description
6618
       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
6619
       * `data-`) to its normalized, camelCase form.
6620
       *
6621
       * Also there is special case for Moz prefix starting with upper case letter.
6622
       *
6623
       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
6624
       *
6625
       * @param {string} name Name to normalize
6626
       */
6627
      $normalize: directiveNormalize,
6628
 
6629
 
6630
      /**
6631
       * @ngdoc method
6632
       * @name $compile.directive.Attributes#$addClass
6633
       * @kind function
6634
       *
6635
       * @description
6636
       * Adds the CSS class value specified by the classVal parameter to the element. If animations
6637
       * are enabled then an animation will be triggered for the class addition.
6638
       *
6639
       * @param {string} classVal The className value that will be added to the element
6640
       */
6641
      $addClass: function(classVal) {
6642
        if (classVal && classVal.length > 0) {
6643
          $animate.addClass(this.$$element, classVal);
6644
        }
6645
      },
6646
 
6647
      /**
6648
       * @ngdoc method
6649
       * @name $compile.directive.Attributes#$removeClass
6650
       * @kind function
6651
       *
6652
       * @description
6653
       * Removes the CSS class value specified by the classVal parameter from the element. If
6654
       * animations are enabled then an animation will be triggered for the class removal.
6655
       *
6656
       * @param {string} classVal The className value that will be removed from the element
6657
       */
6658
      $removeClass: function(classVal) {
6659
        if (classVal && classVal.length > 0) {
6660
          $animate.removeClass(this.$$element, classVal);
6661
        }
6662
      },
6663
 
6664
      /**
6665
       * @ngdoc method
6666
       * @name $compile.directive.Attributes#$updateClass
6667
       * @kind function
6668
       *
6669
       * @description
6670
       * Adds and removes the appropriate CSS class values to the element based on the difference
6671
       * between the new and old CSS class values (specified as newClasses and oldClasses).
6672
       *
6673
       * @param {string} newClasses The current CSS className value
6674
       * @param {string} oldClasses The former CSS className value
6675
       */
6676
      $updateClass: function(newClasses, oldClasses) {
6677
        var toAdd = tokenDifference(newClasses, oldClasses);
6678
        if (toAdd && toAdd.length) {
6679
          $animate.addClass(this.$$element, toAdd);
6680
        }
6681
 
6682
        var toRemove = tokenDifference(oldClasses, newClasses);
6683
        if (toRemove && toRemove.length) {
6684
          $animate.removeClass(this.$$element, toRemove);
6685
        }
6686
      },
6687
 
6688
      /**
6689
       * Set a normalized attribute on the element in a way such that all directives
6690
       * can share the attribute. This function properly handles boolean attributes.
6691
       * @param {string} key Normalized key. (ie ngAttribute)
6692
       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
6693
       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
6694
       *     Defaults to true.
6695
       * @param {string=} attrName Optional none normalized name. Defaults to key.
6696
       */
6697
      $set: function(key, value, writeAttr, attrName) {
6698
        // TODO: decide whether or not to throw an error if "class"
6699
        //is set through this function since it may cause $updateClass to
6700
        //become unstable.
6701
 
6702
        var node = this.$$element[0],
6703
            booleanKey = getBooleanAttrName(node, key),
6704
            aliasedKey = getAliasedAttrName(node, key),
6705
            observer = key,
6706
            nodeName;
6707
 
6708
        if (booleanKey) {
6709
          this.$$element.prop(key, value);
6710
          attrName = booleanKey;
6711
        } else if (aliasedKey) {
6712
          this[aliasedKey] = value;
6713
          observer = aliasedKey;
6714
        }
6715
 
6716
        this[key] = value;
6717
 
6718
        // translate normalized key to actual key
6719
        if (attrName) {
6720
          this.$attr[key] = attrName;
6721
        } else {
6722
          attrName = this.$attr[key];
6723
          if (!attrName) {
6724
            this.$attr[key] = attrName = snake_case(key, '-');
6725
          }
6726
        }
6727
 
6728
        nodeName = nodeName_(this.$$element);
6729
 
6730
        if ((nodeName === 'a' && key === 'href') ||
6731
            (nodeName === 'img' && key === 'src')) {
6732
          // sanitize a[href] and img[src] values
6733
          this[key] = value = $$sanitizeUri(value, key === 'src');
6734
        } else if (nodeName === 'img' && key === 'srcset') {
6735
          // sanitize img[srcset] values
6736
          var result = "";
6737
 
6738
          // first check if there are spaces because it's not the same pattern
6739
          var trimmedSrcset = trim(value);
6740
          //                (   999x   ,|   999w   ,|   ,|,   )
6741
          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
6742
          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
6743
 
6744
          // split srcset into tuple of uri and descriptor except for the last item
6745
          var rawUris = trimmedSrcset.split(pattern);
6746
 
6747
          // for each tuples
6748
          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6749
          for (var i = 0; i < nbrUrisWith2parts; i++) {
6750
            var innerIdx = i * 2;
6751
            // sanitize the uri
6752
            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6753
            // add the descriptor
6754
            result += (" " + trim(rawUris[innerIdx + 1]));
6755
          }
6756
 
6757
          // split the last item into uri and descriptor
6758
          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6759
 
6760
          // sanitize the last uri
6761
          result += $$sanitizeUri(trim(lastTuple[0]), true);
6762
 
6763
          // and add the last descriptor if any
6764
          if (lastTuple.length === 2) {
6765
            result += (" " + trim(lastTuple[1]));
6766
          }
6767
          this[key] = value = result;
6768
        }
6769
 
6770
        if (writeAttr !== false) {
6771
          if (value === null || value === undefined) {
6772
            this.$$element.removeAttr(attrName);
6773
          } else {
6774
            this.$$element.attr(attrName, value);
6775
          }
6776
        }
6777
 
6778
        // fire observers
6779
        var $$observers = this.$$observers;
6780
        $$observers && forEach($$observers[observer], function(fn) {
6781
          try {
6782
            fn(value);
6783
          } catch (e) {
6784
            $exceptionHandler(e);
6785
          }
6786
        });
6787
      },
6788
 
6789
 
6790
      /**
6791
       * @ngdoc method
6792
       * @name $compile.directive.Attributes#$observe
6793
       * @kind function
6794
       *
6795
       * @description
6796
       * Observes an interpolated attribute.
6797
       *
6798
       * The observer function will be invoked once during the next `$digest` following
6799
       * compilation. The observer is then invoked whenever the interpolated value
6800
       * changes.
6801
       *
6802
       * @param {string} key Normalized key. (ie ngAttribute) .
6803
       * @param {function(interpolatedValue)} fn Function that will be called whenever
6804
                the interpolated value of the attribute changes.
6805
       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
6806
       * @returns {function()} Returns a deregistration function for this observer.
6807
       */
6808
      $observe: function(key, fn) {
6809
        var attrs = this,
6810
            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
6811
            listeners = ($$observers[key] || ($$observers[key] = []));
6812
 
6813
        listeners.push(fn);
6814
        $rootScope.$evalAsync(function() {
6815
          if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
6816
            // no one registered attribute interpolation function, so lets call it manually
6817
            fn(attrs[key]);
6818
          }
6819
        });
6820
 
6821
        return function() {
6822
          arrayRemove(listeners, fn);
6823
        };
6824
      }
6825
    };
6826
 
6827
 
6828
    function safeAddClass($element, className) {
6829
      try {
6830
        $element.addClass(className);
6831
      } catch (e) {
6832
        // ignore, since it means that we are trying to set class on
6833
        // SVG element, where class name is read-only.
6834
      }
6835
    }
6836
 
6837
 
6838
    var startSymbol = $interpolate.startSymbol(),
6839
        endSymbol = $interpolate.endSymbol(),
6840
        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
6841
            ? identity
6842
            : function denormalizeTemplate(template) {
6843
              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
6844
        },
6845
        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
6846
 
6847
    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
6848
      var bindings = $element.data('$binding') || [];
6849
 
6850
      if (isArray(binding)) {
6851
        bindings = bindings.concat(binding);
6852
      } else {
6853
        bindings.push(binding);
6854
      }
6855
 
6856
      $element.data('$binding', bindings);
6857
    } : noop;
6858
 
6859
    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
6860
      safeAddClass($element, 'ng-binding');
6861
    } : noop;
6862
 
6863
    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
6864
      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
6865
      $element.data(dataName, scope);
6866
    } : noop;
6867
 
6868
    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
6869
      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
6870
    } : noop;
6871
 
6872
    return compile;
6873
 
6874
    //================================
6875
 
6876
    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
6877
                        previousCompileContext) {
6878
      if (!($compileNodes instanceof jqLite)) {
6879
        // jquery always rewraps, whereas we need to preserve the original selector so that we can
6880
        // modify it.
6881
        $compileNodes = jqLite($compileNodes);
6882
      }
6883
      // We can not compile top level text elements since text nodes can be merged and we will
6884
      // not be able to attach scope data to them, so we will wrap them in <span>
6885
      forEach($compileNodes, function(node, index) {
6886
        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
6887
          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
6888
        }
6889
      });
6890
      var compositeLinkFn =
6891
              compileNodes($compileNodes, transcludeFn, $compileNodes,
6892
                           maxPriority, ignoreDirective, previousCompileContext);
6893
      compile.$$addScopeClass($compileNodes);
6894
      var namespace = null;
6895
      return function publicLinkFn(scope, cloneConnectFn, options) {
6896
        assertArg(scope, 'scope');
6897
 
6898
        options = options || {};
6899
        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
6900
          transcludeControllers = options.transcludeControllers,
6901
          futureParentElement = options.futureParentElement;
6902
 
6903
        // When `parentBoundTranscludeFn` is passed, it is a
6904
        // `controllersBoundTransclude` function (it was previously passed
6905
        // as `transclude` to directive.link) so we must unwrap it to get
6906
        // its `boundTranscludeFn`
6907
        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
6908
          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
6909
        }
6910
 
6911
        if (!namespace) {
6912
          namespace = detectNamespaceForChildElements(futureParentElement);
6913
        }
6914
        var $linkNode;
6915
        if (namespace !== 'html') {
6916
          // When using a directive with replace:true and templateUrl the $compileNodes
6917
          // (or a child element inside of them)
6918
          // might change, so we need to recreate the namespace adapted compileNodes
6919
          // for call to the link function.
6920
          // Note: This will already clone the nodes...
6921
          $linkNode = jqLite(
6922
            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
6923
          );
6924
        } else if (cloneConnectFn) {
6925
          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
6926
          // and sometimes changes the structure of the DOM.
6927
          $linkNode = JQLitePrototype.clone.call($compileNodes);
6928
        } else {
6929
          $linkNode = $compileNodes;
6930
        }
6931
 
6932
        if (transcludeControllers) {
6933
          for (var controllerName in transcludeControllers) {
6934
            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
6935
          }
6936
        }
6937
 
6938
        compile.$$addScopeInfo($linkNode, scope);
6939
 
6940
        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
6941
        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
6942
        return $linkNode;
6943
      };
6944
    }
6945
 
6946
    function detectNamespaceForChildElements(parentElement) {
6947
      // TODO: Make this detect MathML as well...
6948
      var node = parentElement && parentElement[0];
6949
      if (!node) {
6950
        return 'html';
6951
      } else {
6952
        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
6953
      }
6954
    }
6955
 
6956
    /**
6957
     * Compile function matches each node in nodeList against the directives. Once all directives
6958
     * for a particular node are collected their compile functions are executed. The compile
6959
     * functions return values - the linking functions - are combined into a composite linking
6960
     * function, which is the a linking function for the node.
6961
     *
6962
     * @param {NodeList} nodeList an array of nodes or NodeList to compile
6963
     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
6964
     *        scope argument is auto-generated to the new child of the transcluded parent scope.
6965
     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
6966
     *        the rootElement must be set the jqLite collection of the compile root. This is
6967
     *        needed so that the jqLite collection items can be replaced with widgets.
6968
     * @param {number=} maxPriority Max directive priority.
6969
     * @returns {Function} A composite linking function of all of the matched directives or null.
6970
     */
6971
    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
6972
                            previousCompileContext) {
6973
      var linkFns = [],
6974
          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
6975
 
6976
      for (var i = 0; i < nodeList.length; i++) {
6977
        attrs = new Attributes();
6978
 
6979
        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
6980
        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
6981
                                        ignoreDirective);
6982
 
6983
        nodeLinkFn = (directives.length)
6984
            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
6985
                                      null, [], [], previousCompileContext)
6986
            : null;
6987
 
6988
        if (nodeLinkFn && nodeLinkFn.scope) {
6989
          compile.$$addScopeClass(attrs.$$element);
6990
        }
6991
 
6992
        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
6993
                      !(childNodes = nodeList[i].childNodes) ||
6994
                      !childNodes.length)
6995
            ? null
6996
            : compileNodes(childNodes,
6997
                 nodeLinkFn ? (
6998
                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
6999
                     && nodeLinkFn.transclude) : transcludeFn);
7000
 
7001
        if (nodeLinkFn || childLinkFn) {
7002
          linkFns.push(i, nodeLinkFn, childLinkFn);
7003
          linkFnFound = true;
7004
          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7005
        }
7006
 
7007
        //use the previous context only for the first element in the virtual group
7008
        previousCompileContext = null;
7009
      }
7010
 
7011
      // return a linking function if we have found anything, null otherwise
7012
      return linkFnFound ? compositeLinkFn : null;
7013
 
7014
      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7015
        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7016
        var stableNodeList;
7017
 
7018
 
7019
        if (nodeLinkFnFound) {
7020
          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7021
          // offsets don't get screwed up
7022
          var nodeListLength = nodeList.length;
7023
          stableNodeList = new Array(nodeListLength);
7024
 
7025
          // create a sparse array by only copying the elements which have a linkFn
7026
          for (i = 0; i < linkFns.length; i+=3) {
7027
            idx = linkFns[i];
7028
            stableNodeList[idx] = nodeList[idx];
7029
          }
7030
        } else {
7031
          stableNodeList = nodeList;
7032
        }
7033
 
7034
        for (i = 0, ii = linkFns.length; i < ii;) {
7035
          node = stableNodeList[linkFns[i++]];
7036
          nodeLinkFn = linkFns[i++];
7037
          childLinkFn = linkFns[i++];
7038
 
7039
          if (nodeLinkFn) {
7040
            if (nodeLinkFn.scope) {
7041
              childScope = scope.$new();
7042
              compile.$$addScopeInfo(jqLite(node), childScope);
7043
            } else {
7044
              childScope = scope;
7045
            }
7046
 
7047
            if (nodeLinkFn.transcludeOnThisElement) {
7048
              childBoundTranscludeFn = createBoundTranscludeFn(
7049
                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
7050
                  nodeLinkFn.elementTranscludeOnThisElement);
7051
 
7052
            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7053
              childBoundTranscludeFn = parentBoundTranscludeFn;
7054
 
7055
            } else if (!parentBoundTranscludeFn && transcludeFn) {
7056
              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7057
 
7058
            } else {
7059
              childBoundTranscludeFn = null;
7060
            }
7061
 
7062
            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7063
 
7064
          } else if (childLinkFn) {
7065
            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7066
          }
7067
        }
7068
      }
7069
    }
7070
 
7071
    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
7072
 
7073
      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7074
 
7075
        if (!transcludedScope) {
7076
          transcludedScope = scope.$new(false, containingScope);
7077
          transcludedScope.$$transcluded = true;
7078
        }
7079
 
7080
        return transcludeFn(transcludedScope, cloneFn, {
7081
          parentBoundTranscludeFn: previousBoundTranscludeFn,
7082
          transcludeControllers: controllers,
7083
          futureParentElement: futureParentElement
7084
        });
7085
      };
7086
 
7087
      return boundTranscludeFn;
7088
    }
7089
 
7090
    /**
7091
     * Looks for directives on the given node and adds them to the directive collection which is
7092
     * sorted.
7093
     *
7094
     * @param node Node to search.
7095
     * @param directives An array to which the directives are added to. This array is sorted before
7096
     *        the function returns.
7097
     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7098
     * @param {number=} maxPriority Max directive priority.
7099
     */
7100
    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7101
      var nodeType = node.nodeType,
7102
          attrsMap = attrs.$attr,
7103
          match,
7104
          className;
7105
 
7106
      switch (nodeType) {
7107
        case NODE_TYPE_ELEMENT: /* Element */
7108
          // use the node name: <directive>
7109
          addDirective(directives,
7110
              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7111
 
7112
          // iterate over the attributes
7113
          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7114
                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7115
            var attrStartName = false;
7116
            var attrEndName = false;
7117
 
7118
            attr = nAttrs[j];
7119
            name = attr.name;
7120
            value = trim(attr.value);
7121
 
7122
            // support ngAttr attribute binding
7123
            ngAttrName = directiveNormalize(name);
7124
            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7125
              name = snake_case(ngAttrName.substr(6), '-');
7126
            }
7127
 
7128
            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7129
            if (directiveIsMultiElement(directiveNName)) {
7130
              if (ngAttrName === directiveNName + 'Start') {
7131
                attrStartName = name;
7132
                attrEndName = name.substr(0, name.length - 5) + 'end';
7133
                name = name.substr(0, name.length - 6);
7134
              }
7135
            }
7136
 
7137
            nName = directiveNormalize(name.toLowerCase());
7138
            attrsMap[nName] = name;
7139
            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7140
                attrs[nName] = value;
7141
                if (getBooleanAttrName(node, nName)) {
7142
                  attrs[nName] = true; // presence means true
7143
                }
7144
            }
7145
            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7146
            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7147
                          attrEndName);
7148
          }
7149
 
7150
          // use class as directive
7151
          className = node.className;
7152
          if (isString(className) && className !== '') {
7153
            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7154
              nName = directiveNormalize(match[2]);
7155
              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7156
                attrs[nName] = trim(match[3]);
7157
              }
7158
              className = className.substr(match.index + match[0].length);
7159
            }
7160
          }
7161
          break;
7162
        case NODE_TYPE_TEXT: /* Text Node */
7163
          addTextInterpolateDirective(directives, node.nodeValue);
7164
          break;
7165
        case NODE_TYPE_COMMENT: /* Comment */
7166
          try {
7167
            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7168
            if (match) {
7169
              nName = directiveNormalize(match[1]);
7170
              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7171
                attrs[nName] = trim(match[2]);
7172
              }
7173
            }
7174
          } catch (e) {
7175
            // turns out that under some circumstances IE9 throws errors when one attempts to read
7176
            // comment's node value.
7177
            // Just ignore it and continue. (Can't seem to reproduce in test case.)
7178
          }
7179
          break;
7180
      }
7181
 
7182
      directives.sort(byPriority);
7183
      return directives;
7184
    }
7185
 
7186
    /**
7187
     * Given a node with an directive-start it collects all of the siblings until it finds
7188
     * directive-end.
7189
     * @param node
7190
     * @param attrStart
7191
     * @param attrEnd
7192
     * @returns {*}
7193
     */
7194
    function groupScan(node, attrStart, attrEnd) {
7195
      var nodes = [];
7196
      var depth = 0;
7197
      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7198
        do {
7199
          if (!node) {
7200
            throw $compileMinErr('uterdir',
7201
                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7202
                      attrStart, attrEnd);
7203
          }
7204
          if (node.nodeType == NODE_TYPE_ELEMENT) {
7205
            if (node.hasAttribute(attrStart)) depth++;
7206
            if (node.hasAttribute(attrEnd)) depth--;
7207
          }
7208
          nodes.push(node);
7209
          node = node.nextSibling;
7210
        } while (depth > 0);
7211
      } else {
7212
        nodes.push(node);
7213
      }
7214
 
7215
      return jqLite(nodes);
7216
    }
7217
 
7218
    /**
7219
     * Wrapper for linking function which converts normal linking function into a grouped
7220
     * linking function.
7221
     * @param linkFn
7222
     * @param attrStart
7223
     * @param attrEnd
7224
     * @returns {Function}
7225
     */
7226
    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7227
      return function(scope, element, attrs, controllers, transcludeFn) {
7228
        element = groupScan(element[0], attrStart, attrEnd);
7229
        return linkFn(scope, element, attrs, controllers, transcludeFn);
7230
      };
7231
    }
7232
 
7233
    /**
7234
     * Once the directives have been collected, their compile functions are executed. This method
7235
     * is responsible for inlining directive templates as well as terminating the application
7236
     * of the directives if the terminal directive has been reached.
7237
     *
7238
     * @param {Array} directives Array of collected directives to execute their compile function.
7239
     *        this needs to be pre-sorted by priority order.
7240
     * @param {Node} compileNode The raw DOM node to apply the compile functions to
7241
     * @param {Object} templateAttrs The shared attribute function
7242
     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7243
     *                                                  scope argument is auto-generated to the new
7244
     *                                                  child of the transcluded parent scope.
7245
     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7246
     *                              argument has the root jqLite array so that we can replace nodes
7247
     *                              on it.
7248
     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7249
     *                                           compiling the transclusion.
7250
     * @param {Array.<Function>} preLinkFns
7251
     * @param {Array.<Function>} postLinkFns
7252
     * @param {Object} previousCompileContext Context used for previous compilation of the current
7253
     *                                        node
7254
     * @returns {Function} linkFn
7255
     */
7256
    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7257
                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7258
                                   previousCompileContext) {
7259
      previousCompileContext = previousCompileContext || {};
7260
 
7261
      var terminalPriority = -Number.MAX_VALUE,
7262
          newScopeDirective,
7263
          controllerDirectives = previousCompileContext.controllerDirectives,
7264
          controllers,
7265
          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7266
          templateDirective = previousCompileContext.templateDirective,
7267
          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7268
          hasTranscludeDirective = false,
7269
          hasTemplate = false,
7270
          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7271
          $compileNode = templateAttrs.$$element = jqLite(compileNode),
7272
          directive,
7273
          directiveName,
7274
          $template,
7275
          replaceDirective = originalReplaceDirective,
7276
          childTranscludeFn = transcludeFn,
7277
          linkFn,
7278
          directiveValue;
7279
 
7280
      // executes all directives on the current element
7281
      for (var i = 0, ii = directives.length; i < ii; i++) {
7282
        directive = directives[i];
7283
        var attrStart = directive.$$start;
7284
        var attrEnd = directive.$$end;
7285
 
7286
        // collect multiblock sections
7287
        if (attrStart) {
7288
          $compileNode = groupScan(compileNode, attrStart, attrEnd);
7289
        }
7290
        $template = undefined;
7291
 
7292
        if (terminalPriority > directive.priority) {
7293
          break; // prevent further processing of directives
7294
        }
7295
 
7296
        if (directiveValue = directive.scope) {
7297
 
7298
          // skip the check for directives with async templates, we'll check the derived sync
7299
          // directive when the template arrives
7300
          if (!directive.templateUrl) {
7301
            if (isObject(directiveValue)) {
7302
              // This directive is trying to add an isolated scope.
7303
              // Check that there is no scope of any kind already
7304
              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7305
                                directive, $compileNode);
7306
              newIsolateScopeDirective = directive;
7307
            } else {
7308
              // This directive is trying to add a child scope.
7309
              // Check that there is no isolated scope already
7310
              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7311
                                $compileNode);
7312
            }
7313
          }
7314
 
7315
          newScopeDirective = newScopeDirective || directive;
7316
        }
7317
 
7318
        directiveName = directive.name;
7319
 
7320
        if (!directive.templateUrl && directive.controller) {
7321
          directiveValue = directive.controller;
7322
          controllerDirectives = controllerDirectives || {};
7323
          assertNoDuplicate("'" + directiveName + "' controller",
7324
              controllerDirectives[directiveName], directive, $compileNode);
7325
          controllerDirectives[directiveName] = directive;
7326
        }
7327
 
7328
        if (directiveValue = directive.transclude) {
7329
          hasTranscludeDirective = true;
7330
 
7331
          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7332
          // This option should only be used by directives that know how to safely handle element transclusion,
7333
          // where the transcluded nodes are added or replaced after linking.
7334
          if (!directive.$$tlb) {
7335
            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7336
            nonTlbTranscludeDirective = directive;
7337
          }
7338
 
7339
          if (directiveValue == 'element') {
7340
            hasElementTranscludeDirective = true;
7341
            terminalPriority = directive.priority;
7342
            $template = $compileNode;
7343
            $compileNode = templateAttrs.$$element =
7344
                jqLite(document.createComment(' ' + directiveName + ': ' +
7345
                                              templateAttrs[directiveName] + ' '));
7346
            compileNode = $compileNode[0];
7347
            replaceWith(jqCollection, sliceArgs($template), compileNode);
7348
 
7349
            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7350
                                        replaceDirective && replaceDirective.name, {
7351
                                          // Don't pass in:
7352
                                          // - controllerDirectives - otherwise we'll create duplicates controllers
7353
                                          // - newIsolateScopeDirective or templateDirective - combining templates with
7354
                                          //   element transclusion doesn't make sense.
7355
                                          //
7356
                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7357
                                          // on the same element more than once.
7358
                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
7359
                                        });
7360
          } else {
7361
            $template = jqLite(jqLiteClone(compileNode)).contents();
7362
            $compileNode.empty(); // clear contents
7363
            childTranscludeFn = compile($template, transcludeFn);
7364
          }
7365
        }
7366
 
7367
        if (directive.template) {
7368
          hasTemplate = true;
7369
          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7370
          templateDirective = directive;
7371
 
7372
          directiveValue = (isFunction(directive.template))
7373
              ? directive.template($compileNode, templateAttrs)
7374
              : directive.template;
7375
 
7376
          directiveValue = denormalizeTemplate(directiveValue);
7377
 
7378
          if (directive.replace) {
7379
            replaceDirective = directive;
7380
            if (jqLiteIsTextNode(directiveValue)) {
7381
              $template = [];
7382
            } else {
7383
              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7384
            }
7385
            compileNode = $template[0];
7386
 
7387
            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7388
              throw $compileMinErr('tplrt',
7389
                  "Template for directive '{0}' must have exactly one root element. {1}",
7390
                  directiveName, '');
7391
            }
7392
 
7393
            replaceWith(jqCollection, $compileNode, compileNode);
7394
 
7395
            var newTemplateAttrs = {$attr: {}};
7396
 
7397
            // combine directives from the original node and from the template:
7398
            // - take the array of directives for this element
7399
            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7400
            // - collect directives from the template and sort them by priority
7401
            // - combine directives as: processed + template + unprocessed
7402
            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7403
            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7404
 
7405
            if (newIsolateScopeDirective) {
7406
              markDirectivesAsIsolate(templateDirectives);
7407
            }
7408
            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7409
            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7410
 
7411
            ii = directives.length;
7412
          } else {
7413
            $compileNode.html(directiveValue);
7414
          }
7415
        }
7416
 
7417
        if (directive.templateUrl) {
7418
          hasTemplate = true;
7419
          assertNoDuplicate('template', templateDirective, directive, $compileNode);
7420
          templateDirective = directive;
7421
 
7422
          if (directive.replace) {
7423
            replaceDirective = directive;
7424
          }
7425
 
7426
          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7427
              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7428
                controllerDirectives: controllerDirectives,
7429
                newIsolateScopeDirective: newIsolateScopeDirective,
7430
                templateDirective: templateDirective,
7431
                nonTlbTranscludeDirective: nonTlbTranscludeDirective
7432
              });
7433
          ii = directives.length;
7434
        } else if (directive.compile) {
7435
          try {
7436
            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7437
            if (isFunction(linkFn)) {
7438
              addLinkFns(null, linkFn, attrStart, attrEnd);
7439
            } else if (linkFn) {
7440
              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7441
            }
7442
          } catch (e) {
7443
            $exceptionHandler(e, startingTag($compileNode));
7444
          }
7445
        }
7446
 
7447
        if (directive.terminal) {
7448
          nodeLinkFn.terminal = true;
7449
          terminalPriority = Math.max(terminalPriority, directive.priority);
7450
        }
7451
 
7452
      }
7453
 
7454
      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7455
      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7456
      nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
7457
      nodeLinkFn.templateOnThisElement = hasTemplate;
7458
      nodeLinkFn.transclude = childTranscludeFn;
7459
 
7460
      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7461
 
7462
      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7463
      return nodeLinkFn;
7464
 
7465
      ////////////////////
7466
 
7467
      function addLinkFns(pre, post, attrStart, attrEnd) {
7468
        if (pre) {
7469
          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7470
          pre.require = directive.require;
7471
          pre.directiveName = directiveName;
7472
          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7473
            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7474
          }
7475
          preLinkFns.push(pre);
7476
        }
7477
        if (post) {
7478
          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7479
          post.require = directive.require;
7480
          post.directiveName = directiveName;
7481
          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7482
            post = cloneAndAnnotateFn(post, {isolateScope: true});
7483
          }
7484
          postLinkFns.push(post);
7485
        }
7486
      }
7487
 
7488
 
7489
      function getControllers(directiveName, require, $element, elementControllers) {
7490
        var value, retrievalMethod = 'data', optional = false;
7491
        var $searchElement = $element;
7492
        var match;
7493
        if (isString(require)) {
7494
          match = require.match(REQUIRE_PREFIX_REGEXP);
7495
          require = require.substring(match[0].length);
7496
 
7497
          if (match[3]) {
7498
            if (match[1]) match[3] = null;
7499
            else match[1] = match[3];
7500
          }
7501
          if (match[1] === '^') {
7502
            retrievalMethod = 'inheritedData';
7503
          } else if (match[1] === '^^') {
7504
            retrievalMethod = 'inheritedData';
7505
            $searchElement = $element.parent();
7506
          }
7507
          if (match[2] === '?') {
7508
            optional = true;
7509
          }
7510
 
7511
          value = null;
7512
 
7513
          if (elementControllers && retrievalMethod === 'data') {
7514
            if (value = elementControllers[require]) {
7515
              value = value.instance;
7516
            }
7517
          }
7518
          value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
7519
 
7520
          if (!value && !optional) {
7521
            throw $compileMinErr('ctreq',
7522
                "Controller '{0}', required by directive '{1}', can't be found!",
7523
                require, directiveName);
7524
          }
7525
          return value || null;
7526
        } else if (isArray(require)) {
7527
          value = [];
7528
          forEach(require, function(require) {
7529
            value.push(getControllers(directiveName, require, $element, elementControllers));
7530
          });
7531
        }
7532
        return value;
7533
      }
7534
 
7535
 
7536
      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
7537
        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
7538
            attrs;
7539
 
7540
        if (compileNode === linkNode) {
7541
          attrs = templateAttrs;
7542
          $element = templateAttrs.$$element;
7543
        } else {
7544
          $element = jqLite(linkNode);
7545
          attrs = new Attributes($element, templateAttrs);
7546
        }
7547
 
7548
        if (newIsolateScopeDirective) {
7549
          isolateScope = scope.$new(true);
7550
        }
7551
 
7552
        if (boundTranscludeFn) {
7553
          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
7554
          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
7555
          transcludeFn = controllersBoundTransclude;
7556
          transcludeFn.$$boundTransclude = boundTranscludeFn;
7557
        }
7558
 
7559
        if (controllerDirectives) {
7560
          // TODO: merge `controllers` and `elementControllers` into single object.
7561
          controllers = {};
7562
          elementControllers = {};
7563
          forEach(controllerDirectives, function(directive) {
7564
            var locals = {
7565
              $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
7566
              $element: $element,
7567
              $attrs: attrs,
7568
              $transclude: transcludeFn
7569
            }, controllerInstance;
7570
 
7571
            controller = directive.controller;
7572
            if (controller == '@') {
7573
              controller = attrs[directive.name];
7574
            }
7575
 
7576
            controllerInstance = $controller(controller, locals, true, directive.controllerAs);
7577
 
7578
            // For directives with element transclusion the element is a comment,
7579
            // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
7580
            // clean up (http://bugs.jquery.com/ticket/8335).
7581
            // Instead, we save the controllers for the element in a local hash and attach to .data
7582
            // later, once we have the actual element.
7583
            elementControllers[directive.name] = controllerInstance;
7584
            if (!hasElementTranscludeDirective) {
7585
              $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
7586
            }
7587
 
7588
            controllers[directive.name] = controllerInstance;
7589
          });
7590
        }
7591
 
7592
        if (newIsolateScopeDirective) {
7593
          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
7594
              templateDirective === newIsolateScopeDirective.$$originalDirective)));
7595
          compile.$$addScopeClass($element, true);
7596
 
7597
          var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
7598
          var isolateBindingContext = isolateScope;
7599
          if (isolateScopeController && isolateScopeController.identifier &&
7600
              newIsolateScopeDirective.bindToController === true) {
7601
            isolateBindingContext = isolateScopeController.instance;
7602
          }
7603
 
7604
          forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
7605
            var attrName = definition.attrName,
7606
                optional = definition.optional,
7607
                mode = definition.mode, // @, =, or &
7608
                lastValue,
7609
                parentGet, parentSet, compare;
7610
 
7611
            switch (mode) {
7612
 
7613
              case '@':
7614
                attrs.$observe(attrName, function(value) {
7615
                  isolateBindingContext[scopeName] = value;
7616
                });
7617
                attrs.$$observers[attrName].$$scope = scope;
7618
                if (attrs[attrName]) {
7619
                  // If the attribute has been provided then we trigger an interpolation to ensure
7620
                  // the value is there for use in the link fn
7621
                  isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
7622
                }
7623
                break;
7624
 
7625
              case '=':
7626
                if (optional && !attrs[attrName]) {
7627
                  return;
7628
                }
7629
                parentGet = $parse(attrs[attrName]);
7630
                if (parentGet.literal) {
7631
                  compare = equals;
7632
                } else {
7633
                  compare = function(a, b) { return a === b || (a !== a && b !== b); };
7634
                }
7635
                parentSet = parentGet.assign || function() {
7636
                  // reset the change, or we will throw this exception on every $digest
7637
                  lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7638
                  throw $compileMinErr('nonassign',
7639
                      "Expression '{0}' used with directive '{1}' is non-assignable!",
7640
                      attrs[attrName], newIsolateScopeDirective.name);
7641
                };
7642
                lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7643
                var parentValueWatch = function parentValueWatch(parentValue) {
7644
                  if (!compare(parentValue, isolateBindingContext[scopeName])) {
7645
                    // we are out of sync and need to copy
7646
                    if (!compare(parentValue, lastValue)) {
7647
                      // parent changed and it has precedence
7648
                      isolateBindingContext[scopeName] = parentValue;
7649
                    } else {
7650
                      // if the parent can be assigned then do so
7651
                      parentSet(scope, parentValue = isolateBindingContext[scopeName]);
7652
                    }
7653
                  }
7654
                  return lastValue = parentValue;
7655
                };
7656
                parentValueWatch.$stateful = true;
7657
                var unwatch;
7658
                if (definition.collection) {
7659
                  unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
7660
                } else {
7661
                  unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
7662
                }
7663
                isolateScope.$on('$destroy', unwatch);
7664
                break;
7665
 
7666
              case '&':
7667
                parentGet = $parse(attrs[attrName]);
7668
                isolateBindingContext[scopeName] = function(locals) {
7669
                  return parentGet(scope, locals);
7670
                };
7671
                break;
7672
            }
7673
          });
7674
        }
7675
        if (controllers) {
7676
          forEach(controllers, function(controller) {
7677
            controller();
7678
          });
7679
          controllers = null;
7680
        }
7681
 
7682
        // PRELINKING
7683
        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
7684
          linkFn = preLinkFns[i];
7685
          invokeLinkFn(linkFn,
7686
              linkFn.isolateScope ? isolateScope : scope,
7687
              $element,
7688
              attrs,
7689
              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7690
              transcludeFn
7691
          );
7692
        }
7693
 
7694
        // RECURSION
7695
        // We only pass the isolate scope, if the isolate directive has a template,
7696
        // otherwise the child elements do not belong to the isolate directive.
7697
        var scopeToChild = scope;
7698
        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
7699
          scopeToChild = isolateScope;
7700
        }
7701
        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
7702
 
7703
        // POSTLINKING
7704
        for (i = postLinkFns.length - 1; i >= 0; i--) {
7705
          linkFn = postLinkFns[i];
7706
          invokeLinkFn(linkFn,
7707
              linkFn.isolateScope ? isolateScope : scope,
7708
              $element,
7709
              attrs,
7710
              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7711
              transcludeFn
7712
          );
7713
        }
7714
 
7715
        // This is the function that is injected as `$transclude`.
7716
        // Note: all arguments are optional!
7717
        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
7718
          var transcludeControllers;
7719
 
7720
          // No scope passed in:
7721
          if (!isScope(scope)) {
7722
            futureParentElement = cloneAttachFn;
7723
            cloneAttachFn = scope;
7724
            scope = undefined;
7725
          }
7726
 
7727
          if (hasElementTranscludeDirective) {
7728
            transcludeControllers = elementControllers;
7729
          }
7730
          if (!futureParentElement) {
7731
            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
7732
          }
7733
          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
7734
        }
7735
      }
7736
    }
7737
 
7738
    function markDirectivesAsIsolate(directives) {
7739
      // mark all directives as needing isolate scope.
7740
      for (var j = 0, jj = directives.length; j < jj; j++) {
7741
        directives[j] = inherit(directives[j], {$$isolateScope: true});
7742
      }
7743
    }
7744
 
7745
    /**
7746
     * looks up the directive and decorates it with exception handling and proper parameters. We
7747
     * call this the boundDirective.
7748
     *
7749
     * @param {string} name name of the directive to look up.
7750
     * @param {string} location The directive must be found in specific format.
7751
     *   String containing any of theses characters:
7752
     *
7753
     *   * `E`: element name
7754
     *   * `A': attribute
7755
     *   * `C`: class
7756
     *   * `M`: comment
7757
     * @returns {boolean} true if directive was added.
7758
     */
7759
    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
7760
                          endAttrName) {
7761
      if (name === ignoreDirective) return null;
7762
      var match = null;
7763
      if (hasDirectives.hasOwnProperty(name)) {
7764
        for (var directive, directives = $injector.get(name + Suffix),
7765
            i = 0, ii = directives.length; i < ii; i++) {
7766
          try {
7767
            directive = directives[i];
7768
            if ((maxPriority === undefined || maxPriority > directive.priority) &&
7769
                 directive.restrict.indexOf(location) != -1) {
7770
              if (startAttrName) {
7771
                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
7772
              }
7773
              tDirectives.push(directive);
7774
              match = directive;
7775
            }
7776
          } catch (e) { $exceptionHandler(e); }
7777
        }
7778
      }
7779
      return match;
7780
    }
7781
 
7782
 
7783
    /**
7784
     * looks up the directive and returns true if it is a multi-element directive,
7785
     * and therefore requires DOM nodes between -start and -end markers to be grouped
7786
     * together.
7787
     *
7788
     * @param {string} name name of the directive to look up.
7789
     * @returns true if directive was registered as multi-element.
7790
     */
7791
    function directiveIsMultiElement(name) {
7792
      if (hasDirectives.hasOwnProperty(name)) {
7793
        for (var directive, directives = $injector.get(name + Suffix),
7794
            i = 0, ii = directives.length; i < ii; i++) {
7795
          directive = directives[i];
7796
          if (directive.multiElement) {
7797
            return true;
7798
          }
7799
        }
7800
      }
7801
      return false;
7802
    }
7803
 
7804
    /**
7805
     * When the element is replaced with HTML template then the new attributes
7806
     * on the template need to be merged with the existing attributes in the DOM.
7807
     * The desired effect is to have both of the attributes present.
7808
     *
7809
     * @param {object} dst destination attributes (original DOM)
7810
     * @param {object} src source attributes (from the directive template)
7811
     */
7812
    function mergeTemplateAttributes(dst, src) {
7813
      var srcAttr = src.$attr,
7814
          dstAttr = dst.$attr,
7815
          $element = dst.$$element;
7816
 
7817
      // reapply the old attributes to the new element
7818
      forEach(dst, function(value, key) {
7819
        if (key.charAt(0) != '$') {
7820
          if (src[key] && src[key] !== value) {
7821
            value += (key === 'style' ? ';' : ' ') + src[key];
7822
          }
7823
          dst.$set(key, value, true, srcAttr[key]);
7824
        }
7825
      });
7826
 
7827
      // copy the new attributes on the old attrs object
7828
      forEach(src, function(value, key) {
7829
        if (key == 'class') {
7830
          safeAddClass($element, value);
7831
          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
7832
        } else if (key == 'style') {
7833
          $element.attr('style', $element.attr('style') + ';' + value);
7834
          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
7835
          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
7836
          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
7837
          // have an attribute like "has-own-property" or "data-has-own-property", etc.
7838
        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
7839
          dst[key] = value;
7840
          dstAttr[key] = srcAttr[key];
7841
        }
7842
      });
7843
    }
7844
 
7845
 
7846
    function compileTemplateUrl(directives, $compileNode, tAttrs,
7847
        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
7848
      var linkQueue = [],
7849
          afterTemplateNodeLinkFn,
7850
          afterTemplateChildLinkFn,
7851
          beforeTemplateCompileNode = $compileNode[0],
7852
          origAsyncDirective = directives.shift(),
7853
          // The fact that we have to copy and patch the directive seems wrong!
7854
          derivedSyncDirective = extend({}, origAsyncDirective, {
7855
            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
7856
          }),
7857
          templateUrl = (isFunction(origAsyncDirective.templateUrl))
7858
              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
7859
              : origAsyncDirective.templateUrl,
7860
          templateNamespace = origAsyncDirective.templateNamespace;
7861
 
7862
      $compileNode.empty();
7863
 
7864
      $templateRequest($sce.getTrustedResourceUrl(templateUrl))
7865
        .then(function(content) {
7866
          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
7867
 
7868
          content = denormalizeTemplate(content);
7869
 
7870
          if (origAsyncDirective.replace) {
7871
            if (jqLiteIsTextNode(content)) {
7872
              $template = [];
7873
            } else {
7874
              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
7875
            }
7876
            compileNode = $template[0];
7877
 
7878
            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7879
              throw $compileMinErr('tplrt',
7880
                  "Template for directive '{0}' must have exactly one root element. {1}",
7881
                  origAsyncDirective.name, templateUrl);
7882
            }
7883
 
7884
            tempTemplateAttrs = {$attr: {}};
7885
            replaceWith($rootElement, $compileNode, compileNode);
7886
            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
7887
 
7888
            if (isObject(origAsyncDirective.scope)) {
7889
              markDirectivesAsIsolate(templateDirectives);
7890
            }
7891
            directives = templateDirectives.concat(directives);
7892
            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
7893
          } else {
7894
            compileNode = beforeTemplateCompileNode;
7895
            $compileNode.html(content);
7896
          }
7897
 
7898
          directives.unshift(derivedSyncDirective);
7899
 
7900
          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
7901
              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
7902
              previousCompileContext);
7903
          forEach($rootElement, function(node, i) {
7904
            if (node == compileNode) {
7905
              $rootElement[i] = $compileNode[0];
7906
            }
7907
          });
7908
          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
7909
 
7910
          while (linkQueue.length) {
7911
            var scope = linkQueue.shift(),
7912
                beforeTemplateLinkNode = linkQueue.shift(),
7913
                linkRootElement = linkQueue.shift(),
7914
                boundTranscludeFn = linkQueue.shift(),
7915
                linkNode = $compileNode[0];
7916
 
7917
            if (scope.$$destroyed) continue;
7918
 
7919
            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
7920
              var oldClasses = beforeTemplateLinkNode.className;
7921
 
7922
              if (!(previousCompileContext.hasElementTranscludeDirective &&
7923
                  origAsyncDirective.replace)) {
7924
                // it was cloned therefore we have to clone as well.
7925
                linkNode = jqLiteClone(compileNode);
7926
              }
7927
              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
7928
 
7929
              // Copy in CSS classes from original node
7930
              safeAddClass(jqLite(linkNode), oldClasses);
7931
            }
7932
            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7933
              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7934
            } else {
7935
              childBoundTranscludeFn = boundTranscludeFn;
7936
            }
7937
            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
7938
              childBoundTranscludeFn);
7939
          }
7940
          linkQueue = null;
7941
        });
7942
 
7943
      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
7944
        var childBoundTranscludeFn = boundTranscludeFn;
7945
        if (scope.$$destroyed) return;
7946
        if (linkQueue) {
7947
          linkQueue.push(scope,
7948
                         node,
7949
                         rootElement,
7950
                         childBoundTranscludeFn);
7951
        } else {
7952
          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7953
            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7954
          }
7955
          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
7956
        }
7957
      };
7958
    }
7959
 
7960
 
7961
    /**
7962
     * Sorting function for bound directives.
7963
     */
7964
    function byPriority(a, b) {
7965
      var diff = b.priority - a.priority;
7966
      if (diff !== 0) return diff;
7967
      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
7968
      return a.index - b.index;
7969
    }
7970
 
7971
 
7972
    function assertNoDuplicate(what, previousDirective, directive, element) {
7973
      if (previousDirective) {
7974
        throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
7975
            previousDirective.name, directive.name, what, startingTag(element));
7976
      }
7977
    }
7978
 
7979
 
7980
    function addTextInterpolateDirective(directives, text) {
7981
      var interpolateFn = $interpolate(text, true);
7982
      if (interpolateFn) {
7983
        directives.push({
7984
          priority: 0,
7985
          compile: function textInterpolateCompileFn(templateNode) {
7986
            var templateNodeParent = templateNode.parent(),
7987
                hasCompileParent = !!templateNodeParent.length;
7988
 
7989
            // When transcluding a template that has bindings in the root
7990
            // we don't have a parent and thus need to add the class during linking fn.
7991
            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
7992
 
7993
            return function textInterpolateLinkFn(scope, node) {
7994
              var parent = node.parent();
7995
              if (!hasCompileParent) compile.$$addBindingClass(parent);
7996
              compile.$$addBindingInfo(parent, interpolateFn.expressions);
7997
              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
7998
                node[0].nodeValue = value;
7999
              });
8000
            };
8001
          }
8002
        });
8003
      }
8004
    }
8005
 
8006
 
8007
    function wrapTemplate(type, template) {
8008
      type = lowercase(type || 'html');
8009
      switch (type) {
8010
      case 'svg':
8011
      case 'math':
8012
        var wrapper = document.createElement('div');
8013
        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8014
        return wrapper.childNodes[0].childNodes;
8015
      default:
8016
        return template;
8017
      }
8018
    }
8019
 
8020
 
8021
    function getTrustedContext(node, attrNormalizedName) {
8022
      if (attrNormalizedName == "srcdoc") {
8023
        return $sce.HTML;
8024
      }
8025
      var tag = nodeName_(node);
8026
      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8027
      if (attrNormalizedName == "xlinkHref" ||
8028
          (tag == "form" && attrNormalizedName == "action") ||
8029
          (tag != "img" && (attrNormalizedName == "src" ||
8030
                            attrNormalizedName == "ngSrc"))) {
8031
        return $sce.RESOURCE_URL;
8032
      }
8033
    }
8034
 
8035
 
8036
    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8037
      var interpolateFn = $interpolate(value, true);
8038
 
8039
      // no interpolation found -> ignore
8040
      if (!interpolateFn) return;
8041
 
8042
 
8043
      if (name === "multiple" && nodeName_(node) === "select") {
8044
        throw $compileMinErr("selmulti",
8045
            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8046
            startingTag(node));
8047
      }
8048
 
8049
      directives.push({
8050
        priority: 100,
8051
        compile: function() {
8052
            return {
8053
              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8054
                var $$observers = (attr.$$observers || (attr.$$observers = {}));
8055
 
8056
                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8057
                  throw $compileMinErr('nodomevents',
8058
                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8059
                          "ng- versions (such as ng-click instead of onclick) instead.");
8060
                }
8061
 
8062
                // If the attribute was removed, then we are done
8063
                if (!attr[name]) {
8064
                  return;
8065
                }
8066
 
8067
                // we need to interpolate again, in case the attribute value has been updated
8068
                // (e.g. by another directive's compile function)
8069
                interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
8070
                    ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
8071
 
8072
                // if attribute was updated so that there is no interpolation going on we don't want to
8073
                // register any observers
8074
                if (!interpolateFn) return;
8075
 
8076
                // initialize attr object so that it's ready in case we need the value for isolate
8077
                // scope initialization, otherwise the value would not be available from isolate
8078
                // directive's linking fn during linking phase
8079
                attr[name] = interpolateFn(scope);
8080
 
8081
                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8082
                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8083
                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8084
                    //special case for class attribute addition + removal
8085
                    //so that class changes can tap into the animation
8086
                    //hooks provided by the $animate service. Be sure to
8087
                    //skip animations when the first digest occurs (when
8088
                    //both the new and the old values are the same) since
8089
                    //the CSS classes are the non-interpolated values
8090
                    if (name === 'class' && newValue != oldValue) {
8091
                      attr.$updateClass(newValue, oldValue);
8092
                    } else {
8093
                      attr.$set(name, newValue);
8094
                    }
8095
                  });
8096
              }
8097
            };
8098
          }
8099
      });
8100
    }
8101
 
8102
 
8103
    /**
8104
     * This is a special jqLite.replaceWith, which can replace items which
8105
     * have no parents, provided that the containing jqLite collection is provided.
8106
     *
8107
     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8108
     *                               in the root of the tree.
8109
     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8110
     *                                  the shell, but replace its DOM node reference.
8111
     * @param {Node} newNode The new DOM node.
8112
     */
8113
    function replaceWith($rootElement, elementsToRemove, newNode) {
8114
      var firstElementToRemove = elementsToRemove[0],
8115
          removeCount = elementsToRemove.length,
8116
          parent = firstElementToRemove.parentNode,
8117
          i, ii;
8118
 
8119
      if ($rootElement) {
8120
        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8121
          if ($rootElement[i] == firstElementToRemove) {
8122
            $rootElement[i++] = newNode;
8123
            for (var j = i, j2 = j + removeCount - 1,
8124
                     jj = $rootElement.length;
8125
                 j < jj; j++, j2++) {
8126
              if (j2 < jj) {
8127
                $rootElement[j] = $rootElement[j2];
8128
              } else {
8129
                delete $rootElement[j];
8130
              }
8131
            }
8132
            $rootElement.length -= removeCount - 1;
8133
 
8134
            // If the replaced element is also the jQuery .context then replace it
8135
            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8136
            // http://api.jquery.com/context/
8137
            if ($rootElement.context === firstElementToRemove) {
8138
              $rootElement.context = newNode;
8139
            }
8140
            break;
8141
          }
8142
        }
8143
      }
8144
 
8145
      if (parent) {
8146
        parent.replaceChild(newNode, firstElementToRemove);
8147
      }
8148
 
8149
      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8150
      var fragment = document.createDocumentFragment();
8151
      fragment.appendChild(firstElementToRemove);
8152
 
8153
      // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8154
      // data here because there's no public interface in jQuery to do that and copying over
8155
      // event listeners (which is the main use of private data) wouldn't work anyway.
8156
      jqLite(newNode).data(jqLite(firstElementToRemove).data());
8157
 
8158
      // Remove data of the replaced element. We cannot just call .remove()
8159
      // on the element it since that would deallocate scope that is needed
8160
      // for the new node. Instead, remove the data "manually".
8161
      if (!jQuery) {
8162
        delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8163
      } else {
8164
        // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8165
        // the replaced element. The cleanData version monkey-patched by Angular would cause
8166
        // the scope to be trashed and we do need the very same scope to work with the new
8167
        // element. However, we cannot just cache the non-patched version and use it here as
8168
        // that would break if another library patches the method after Angular does (one
8169
        // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8170
        // skipped this one time.
8171
        skipDestroyOnNextJQueryCleanData = true;
8172
        jQuery.cleanData([firstElementToRemove]);
8173
      }
8174
 
8175
      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8176
        var element = elementsToRemove[k];
8177
        jqLite(element).remove(); // must do this way to clean up expando
8178
        fragment.appendChild(element);
8179
        delete elementsToRemove[k];
8180
      }
8181
 
8182
      elementsToRemove[0] = newNode;
8183
      elementsToRemove.length = 1;
8184
    }
8185
 
8186
 
8187
    function cloneAndAnnotateFn(fn, annotation) {
8188
      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8189
    }
8190
 
8191
 
8192
    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8193
      try {
8194
        linkFn(scope, $element, attrs, controllers, transcludeFn);
8195
      } catch (e) {
8196
        $exceptionHandler(e, startingTag($element));
8197
      }
8198
    }
8199
  }];
8200
}
8201
 
8202
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8203
/**
8204
 * Converts all accepted directives format into proper directive name.
8205
 * @param name Name to normalize
8206
 */
8207
function directiveNormalize(name) {
8208
  return camelCase(name.replace(PREFIX_REGEXP, ''));
8209
}
8210
 
8211
/**
8212
 * @ngdoc type
8213
 * @name $compile.directive.Attributes
8214
 *
8215
 * @description
8216
 * A shared object between directive compile / linking functions which contains normalized DOM
8217
 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8218
 * needed since all of these are treated as equivalent in Angular:
8219
 *
8220
 * ```
8221
 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8222
 * ```
8223
 */
8224
 
8225
/**
8226
 * @ngdoc property
8227
 * @name $compile.directive.Attributes#$attr
8228
 *
8229
 * @description
8230
 * A map of DOM element attribute names to the normalized name. This is
8231
 * needed to do reverse lookup from normalized name back to actual name.
8232
 */
8233
 
8234
 
8235
/**
8236
 * @ngdoc method
8237
 * @name $compile.directive.Attributes#$set
8238
 * @kind function
8239
 *
8240
 * @description
8241
 * Set DOM element attribute value.
8242
 *
8243
 *
8244
 * @param {string} name Normalized element attribute name of the property to modify. The name is
8245
 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8246
 *          property to the original name.
8247
 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8248
 */
8249
 
8250
 
8251
 
8252
/**
8253
 * Closure compiler type information
8254
 */
8255
 
8256
function nodesetLinkingFn(
8257
  /* angular.Scope */ scope,
8258
  /* NodeList */ nodeList,
8259
  /* Element */ rootElement,
8260
  /* function(Function) */ boundTranscludeFn
8261
) {}
8262
 
8263
function directiveLinkingFn(
8264
  /* nodesetLinkingFn */ nodesetLinkingFn,
8265
  /* angular.Scope */ scope,
8266
  /* Node */ node,
8267
  /* Element */ rootElement,
8268
  /* function(Function) */ boundTranscludeFn
8269
) {}
8270
 
8271
function tokenDifference(str1, str2) {
8272
  var values = '',
8273
      tokens1 = str1.split(/\s+/),
8274
      tokens2 = str2.split(/\s+/);
8275
 
8276
  outer:
8277
  for (var i = 0; i < tokens1.length; i++) {
8278
    var token = tokens1[i];
8279
    for (var j = 0; j < tokens2.length; j++) {
8280
      if (token == tokens2[j]) continue outer;
8281
    }
8282
    values += (values.length > 0 ? ' ' : '') + token;
8283
  }
8284
  return values;
8285
}
8286
 
8287
function removeComments(jqNodes) {
8288
  jqNodes = jqLite(jqNodes);
8289
  var i = jqNodes.length;
8290
 
8291
  if (i <= 1) {
8292
    return jqNodes;
8293
  }
8294
 
8295
  while (i--) {
8296
    var node = jqNodes[i];
8297
    if (node.nodeType === NODE_TYPE_COMMENT) {
8298
      splice.call(jqNodes, i, 1);
8299
    }
8300
  }
8301
  return jqNodes;
8302
}
8303
 
8304
/**
8305
 * @ngdoc provider
8306
 * @name $controllerProvider
8307
 * @description
8308
 * The {@link ng.$controller $controller service} is used by Angular to create new
8309
 * controllers.
8310
 *
8311
 * This provider allows controller registration via the
8312
 * {@link ng.$controllerProvider#register register} method.
8313
 */
8314
function $ControllerProvider() {
8315
  var controllers = {},
8316
      globals = false,
8317
      CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8318
 
8319
 
8320
  /**
8321
   * @ngdoc method
8322
   * @name $controllerProvider#register
8323
   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8324
   *    the names and the values are the constructors.
8325
   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8326
   *    annotations in the array notation).
8327
   */
8328
  this.register = function(name, constructor) {
8329
    assertNotHasOwnProperty(name, 'controller');
8330
    if (isObject(name)) {
8331
      extend(controllers, name);
8332
    } else {
8333
      controllers[name] = constructor;
8334
    }
8335
  };
8336
 
8337
  /**
8338
   * @ngdoc method
8339
   * @name $controllerProvider#allowGlobals
8340
   * @description If called, allows `$controller` to find controller constructors on `window`
8341
   */
8342
  this.allowGlobals = function() {
8343
    globals = true;
8344
  };
8345
 
8346
 
8347
  this.$get = ['$injector', '$window', function($injector, $window) {
8348
 
8349
    /**
8350
     * @ngdoc service
8351
     * @name $controller
8352
     * @requires $injector
8353
     *
8354
     * @param {Function|string} constructor If called with a function then it's considered to be the
8355
     *    controller constructor function. Otherwise it's considered to be a string which is used
8356
     *    to retrieve the controller constructor using the following steps:
8357
     *
8358
     *    * check if a controller with given name is registered via `$controllerProvider`
8359
     *    * check if evaluating the string on the current scope returns a constructor
8360
     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8361
     *      `window` object (not recommended)
8362
     *
8363
     *    The string can use the `controller as property` syntax, where the controller instance is published
8364
     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8365
     *    to work correctly.
8366
     *
8367
     * @param {Object} locals Injection locals for Controller.
8368
     * @return {Object} Instance of given controller.
8369
     *
8370
     * @description
8371
     * `$controller` service is responsible for instantiating controllers.
8372
     *
8373
     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8374
     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8375
     */
8376
    return function(expression, locals, later, ident) {
8377
      // PRIVATE API:
8378
      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
8379
      //                     If true, $controller will allocate the object with the correct
8380
      //                     prototype chain, but will not invoke the controller until a returned
8381
      //                     callback is invoked.
8382
      //   param `ident` --- An optional label which overrides the label parsed from the controller
8383
      //                     expression, if any.
8384
      var instance, match, constructor, identifier;
8385
      later = later === true;
8386
      if (ident && isString(ident)) {
8387
        identifier = ident;
8388
      }
8389
 
8390
      if (isString(expression)) {
8391
        match = expression.match(CNTRL_REG),
8392
        constructor = match[1],
8393
        identifier = identifier || match[3];
8394
        expression = controllers.hasOwnProperty(constructor)
8395
            ? controllers[constructor]
8396
            : getter(locals.$scope, constructor, true) ||
8397
                (globals ? getter($window, constructor, true) : undefined);
8398
 
8399
        assertArgFn(expression, constructor, true);
8400
      }
8401
 
8402
      if (later) {
8403
        // Instantiate controller later:
8404
        // This machinery is used to create an instance of the object before calling the
8405
        // controller's constructor itself.
8406
        //
8407
        // This allows properties to be added to the controller before the constructor is
8408
        // invoked. Primarily, this is used for isolate scope bindings in $compile.
8409
        //
8410
        // This feature is not intended for use by applications, and is thus not documented
8411
        // publicly.
8412
        // Object creation: http://jsperf.com/create-constructor/2
8413
        var controllerPrototype = (isArray(expression) ?
8414
          expression[expression.length - 1] : expression).prototype;
8415
        instance = Object.create(controllerPrototype);
8416
 
8417
        if (identifier) {
8418
          addIdentifier(locals, identifier, instance, constructor || expression.name);
8419
        }
8420
 
8421
        return extend(function() {
8422
          $injector.invoke(expression, instance, locals, constructor);
8423
          return instance;
8424
        }, {
8425
          instance: instance,
8426
          identifier: identifier
8427
        });
8428
      }
8429
 
8430
      instance = $injector.instantiate(expression, locals, constructor);
8431
 
8432
      if (identifier) {
8433
        addIdentifier(locals, identifier, instance, constructor || expression.name);
8434
      }
8435
 
8436
      return instance;
8437
    };
8438
 
8439
    function addIdentifier(locals, identifier, instance, name) {
8440
      if (!(locals && isObject(locals.$scope))) {
8441
        throw minErr('$controller')('noscp',
8442
          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
8443
          name, identifier);
8444
      }
8445
 
8446
      locals.$scope[identifier] = instance;
8447
    }
8448
  }];
8449
}
8450
 
8451
/**
8452
 * @ngdoc service
8453
 * @name $document
8454
 * @requires $window
8455
 *
8456
 * @description
8457
 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
8458
 *
8459
 * @example
8460
   <example module="documentExample">
8461
     <file name="index.html">
8462
       <div ng-controller="ExampleController">
8463
         <p>$document title: <b ng-bind="title"></b></p>
8464
         <p>window.document title: <b ng-bind="windowTitle"></b></p>
8465
       </div>
8466
     </file>
8467
     <file name="script.js">
8468
       angular.module('documentExample', [])
8469
         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
8470
           $scope.title = $document[0].title;
8471
           $scope.windowTitle = angular.element(window.document)[0].title;
8472
         }]);
8473
     </file>
8474
   </example>
8475
 */
8476
function $DocumentProvider() {
8477
  this.$get = ['$window', function(window) {
8478
    return jqLite(window.document);
8479
  }];
8480
}
8481
 
8482
/**
8483
 * @ngdoc service
8484
 * @name $exceptionHandler
8485
 * @requires ng.$log
8486
 *
8487
 * @description
8488
 * Any uncaught exception in angular expressions is delegated to this service.
8489
 * The default implementation simply delegates to `$log.error` which logs it into
8490
 * the browser console.
8491
 *
8492
 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
8493
 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
8494
 *
8495
 * ## Example:
8496
 *
8497
 * ```js
8498
 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
8499
 *     return function(exception, cause) {
8500
 *       exception.message += ' (caused by "' + cause + '")';
8501
 *       throw exception;
8502
 *     };
8503
 *   });
8504
 * ```
8505
 *
8506
 * This example will override the normal action of `$exceptionHandler`, to make angular
8507
 * exceptions fail hard when they happen, instead of just logging to the console.
8508
 *
8509
 * <hr />
8510
 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
8511
 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
8512
 * (unless executed during a digest).
8513
 *
8514
 * If you wish, you can manually delegate exceptions, e.g.
8515
 * `try { ... } catch(e) { $exceptionHandler(e); }`
8516
 *
8517
 * @param {Error} exception Exception associated with the error.
8518
 * @param {string=} cause optional information about the context in which
8519
 *       the error was thrown.
8520
 *
8521
 */
8522
function $ExceptionHandlerProvider() {
8523
  this.$get = ['$log', function($log) {
8524
    return function(exception, cause) {
8525
      $log.error.apply($log, arguments);
8526
    };
8527
  }];
8528
}
8529
 
8530
var APPLICATION_JSON = 'application/json';
8531
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
8532
var JSON_START = /^\s*(\[|\{[^\{])/;
8533
var JSON_END = /[\}\]]\s*$/;
8534
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
8535
 
8536
function defaultHttpResponseTransform(data, headers) {
8537
  if (isString(data)) {
8538
    // strip json vulnerability protection prefix
8539
    data = data.replace(JSON_PROTECTION_PREFIX, '');
8540
    var contentType = headers('Content-Type');
8541
    if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
8542
        (JSON_START.test(data) && JSON_END.test(data))) {
8543
      data = fromJson(data);
8544
    }
8545
  }
8546
  return data;
8547
}
8548
 
8549
/**
8550
 * Parse headers into key value object
8551
 *
8552
 * @param {string} headers Raw headers as a string
8553
 * @returns {Object} Parsed headers as key value object
8554
 */
8555
function parseHeaders(headers) {
8556
  var parsed = createMap(), key, val, i;
8557
 
8558
  if (!headers) return parsed;
8559
 
8560
  forEach(headers.split('\n'), function(line) {
8561
    i = line.indexOf(':');
8562
    key = lowercase(trim(line.substr(0, i)));
8563
    val = trim(line.substr(i + 1));
8564
 
8565
    if (key) {
8566
      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
8567
    }
8568
  });
8569
 
8570
  return parsed;
8571
}
8572
 
8573
 
8574
/**
8575
 * Returns a function that provides access to parsed headers.
8576
 *
8577
 * Headers are lazy parsed when first requested.
8578
 * @see parseHeaders
8579
 *
8580
 * @param {(string|Object)} headers Headers to provide access to.
8581
 * @returns {function(string=)} Returns a getter function which if called with:
8582
 *
8583
 *   - if called with single an argument returns a single header value or null
8584
 *   - if called with no arguments returns an object containing all headers.
8585
 */
8586
function headersGetter(headers) {
8587
  var headersObj = isObject(headers) ? headers : undefined;
8588
 
8589
  return function(name) {
8590
    if (!headersObj) headersObj =  parseHeaders(headers);
8591
 
8592
    if (name) {
8593
      var value = headersObj[lowercase(name)];
8594
      if (value === void 0) {
8595
        value = null;
8596
      }
8597
      return value;
8598
    }
8599
 
8600
    return headersObj;
8601
  };
8602
}
8603
 
8604
 
8605
/**
8606
 * Chain all given functions
8607
 *
8608
 * This function is used for both request and response transforming
8609
 *
8610
 * @param {*} data Data to transform.
8611
 * @param {function(string=)} headers Http headers getter fn.
8612
 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
8613
 * @returns {*} Transformed data.
8614
 */
8615
function transformData(data, headers, fns) {
8616
  if (isFunction(fns))
8617
    return fns(data, headers);
8618
 
8619
  forEach(fns, function(fn) {
8620
    data = fn(data, headers);
8621
  });
8622
 
8623
  return data;
8624
}
8625
 
8626
 
8627
function isSuccess(status) {
8628
  return 200 <= status && status < 300;
8629
}
8630
 
8631
 
8632
/**
8633
 * @ngdoc provider
8634
 * @name $httpProvider
8635
 * @description
8636
 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
8637
 * */
8638
function $HttpProvider() {
8639
  /**
8640
   * @ngdoc property
8641
   * @name $httpProvider#defaults
8642
   * @description
8643
   *
8644
   * Object containing default values for all {@link ng.$http $http} requests.
8645
   *
8646
   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8647
   * that will provide the cache for all requests who set their `cache` property to `true`.
8648
   * If you set the `default.cache = false` then only requests that specify their own custom
8649
   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8650
   *
8651
   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8652
   * Defaults value is `'XSRF-TOKEN'`.
8653
   *
8654
   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
8655
   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
8656
   *
8657
   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
8658
   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
8659
   * setting default headers.
8660
   *     - **`defaults.headers.common`**
8661
   *     - **`defaults.headers.post`**
8662
   *     - **`defaults.headers.put`**
8663
   *     - **`defaults.headers.patch`**
8664
   *
8665
   **/
8666
  var defaults = this.defaults = {
8667
    // transform incoming response data
8668
    transformResponse: [defaultHttpResponseTransform],
8669
 
8670
    // transform outgoing request data
8671
    transformRequest: [function(d) {
8672
      return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
8673
    }],
8674
 
8675
    // default headers
8676
    headers: {
8677
      common: {
8678
        'Accept': 'application/json, text/plain, */*'
8679
      },
8680
      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8681
      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8682
      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
8683
    },
8684
 
8685
    xsrfCookieName: 'XSRF-TOKEN',
8686
    xsrfHeaderName: 'X-XSRF-TOKEN'
8687
  };
8688
 
8689
  var useApplyAsync = false;
8690
  /**
8691
   * @ngdoc method
8692
   * @name $httpProvider#useApplyAsync
8693
   * @description
8694
   *
8695
   * Configure $http service to combine processing of multiple http responses received at around
8696
   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
8697
   * significant performance improvement for bigger applications that make many HTTP requests
8698
   * concurrently (common during application bootstrap).
8699
   *
8700
   * Defaults to false. If no value is specifed, returns the current configured value.
8701
   *
8702
   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
8703
   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
8704
   *    to load and share the same digest cycle.
8705
   *
8706
   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
8707
   *    otherwise, returns the current configured value.
8708
   **/
8709
  this.useApplyAsync = function(value) {
8710
    if (isDefined(value)) {
8711
      useApplyAsync = !!value;
8712
      return this;
8713
    }
8714
    return useApplyAsync;
8715
  };
8716
 
8717
  /**
8718
   * @ngdoc property
8719
   * @name $httpProvider#interceptors
8720
   * @description
8721
   *
8722
   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
8723
   * pre-processing of request or postprocessing of responses.
8724
   *
8725
   * These service factories are ordered by request, i.e. they are applied in the same order as the
8726
   * array, on request, but reverse order, on response.
8727
   *
8728
   * {@link ng.$http#interceptors Interceptors detailed info}
8729
   **/
8730
  var interceptorFactories = this.interceptors = [];
8731
 
8732
  this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
8733
      function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
8734
 
8735
    var defaultCache = $cacheFactory('$http');
8736
 
8737
    /**
8738
     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
8739
     * The reversal is needed so that we can build up the interception chain around the
8740
     * server request.
8741
     */
8742
    var reversedInterceptors = [];
8743
 
8744
    forEach(interceptorFactories, function(interceptorFactory) {
8745
      reversedInterceptors.unshift(isString(interceptorFactory)
8746
          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
8747
    });
8748
 
8749
    /**
8750
     * @ngdoc service
8751
     * @kind function
8752
     * @name $http
8753
     * @requires ng.$httpBackend
8754
     * @requires $cacheFactory
8755
     * @requires $rootScope
8756
     * @requires $q
8757
     * @requires $injector
8758
     *
8759
     * @description
8760
     * The `$http` service is a core Angular service that facilitates communication with the remote
8761
     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
8762
     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
8763
     *
8764
     * For unit testing applications that use `$http` service, see
8765
     * {@link ngMock.$httpBackend $httpBackend mock}.
8766
     *
8767
     * For a higher level of abstraction, please check out the {@link ngResource.$resource
8768
     * $resource} service.
8769
     *
8770
     * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8771
     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8772
     * it is important to familiarize yourself with these APIs and the guarantees they provide.
8773
     *
8774
     *
8775
     * ## General usage
8776
     * The `$http` service is a function which takes a single argument — a configuration object —
8777
     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}
8778
     * with two $http specific methods: `success` and `error`.
8779
     *
8780
     * ```js
8781
     *   // Simple GET request example :
8782
     *   $http.get('/someUrl').
8783
     *     success(function(data, status, headers, config) {
8784
     *       // this callback will be called asynchronously
8785
     *       // when the response is available
8786
     *     }).
8787
     *     error(function(data, status, headers, config) {
8788
     *       // called asynchronously if an error occurs
8789
     *       // or server returns response with an error status.
8790
     *     });
8791
     * ```
8792
     *
8793
     * ```js
8794
     *   // Simple POST request example (passing data) :
8795
     *   $http.post('/someUrl', {msg:'hello word!'}).
8796
     *     success(function(data, status, headers, config) {
8797
     *       // this callback will be called asynchronously
8798
     *       // when the response is available
8799
     *     }).
8800
     *     error(function(data, status, headers, config) {
8801
     *       // called asynchronously if an error occurs
8802
     *       // or server returns response with an error status.
8803
     *     });
8804
     * ```
8805
     *
8806
     *
8807
     * Since the returned value of calling the $http function is a `promise`, you can also use
8808
     * the `then` method to register callbacks, and these callbacks will receive a single argument –
8809
     * an object representing the response. See the API signature and type info below for more
8810
     * details.
8811
     *
8812
     * A response status code between 200 and 299 is considered a success status and
8813
     * will result in the success callback being called. Note that if the response is a redirect,
8814
     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8815
     * called for such responses.
8816
     *
8817
     * ## Writing Unit Tests that use $http
8818
     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
8819
     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
8820
     * request using trained responses.
8821
     *
8822
     * ```
8823
     * $httpBackend.expectGET(...);
8824
     * $http.get(...);
8825
     * $httpBackend.flush();
8826
     * ```
8827
     *
8828
     * ## Shortcut methods
8829
     *
8830
     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
8831
     * request data must be passed in for POST/PUT requests.
8832
     *
8833
     * ```js
8834
     *   $http.get('/someUrl').success(successCallback);
8835
     *   $http.post('/someUrl', data).success(successCallback);
8836
     * ```
8837
     *
8838
     * Complete list of shortcut methods:
8839
     *
8840
     * - {@link ng.$http#get $http.get}
8841
     * - {@link ng.$http#head $http.head}
8842
     * - {@link ng.$http#post $http.post}
8843
     * - {@link ng.$http#put $http.put}
8844
     * - {@link ng.$http#delete $http.delete}
8845
     * - {@link ng.$http#jsonp $http.jsonp}
8846
     * - {@link ng.$http#patch $http.patch}
8847
     *
8848
     *
8849
     * ## Setting HTTP Headers
8850
     *
8851
     * The $http service will automatically add certain HTTP headers to all requests. These defaults
8852
     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8853
     * object, which currently contains this default configuration:
8854
     *
8855
     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8856
     *   - `Accept: application/json, text/plain, * / *`
8857
     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8858
     *   - `Content-Type: application/json`
8859
     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8860
     *   - `Content-Type: application/json`
8861
     *
8862
     * To add or overwrite these defaults, simply add or remove a property from these configuration
8863
     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8864
     * with the lowercased HTTP method name as the key, e.g.
8865
     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
8866
     *
8867
     * The defaults can also be set at runtime via the `$http.defaults` object in the same
8868
     * fashion. For example:
8869
     *
8870
     * ```
8871
     * module.run(function($http) {
8872
     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
8873
     * });
8874
     * ```
8875
     *
8876
     * In addition, you can supply a `headers` property in the config object passed when
8877
     * calling `$http(config)`, which overrides the defaults without changing them globally.
8878
     *
8879
     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8880
     * Use the `headers` property, setting the desired header to `undefined`. For example:
8881
     *
8882
     * ```js
8883
     * var req = {
8884
     *  method: 'POST',
8885
     *  url: 'http://example.com',
8886
     *  headers: {
8887
     *    'Content-Type': undefined
8888
     *  },
8889
     *  data: { test: 'test' },
8890
     * }
8891
     *
8892
     * $http(req).success(function(){...}).error(function(){...});
8893
     * ```
8894
     *
8895
     * ## Transforming Requests and Responses
8896
     *
8897
     * Both requests and responses can be transformed using transformation functions: `transformRequest`
8898
     * and `transformResponse`. These properties can be a single function that returns
8899
     * the transformed value (`{function(data, headersGetter)`) or an array of such transformation functions,
8900
     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
8901
     *
8902
     * ### Default Transformations
8903
     *
8904
     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
8905
     * `defaults.transformResponse` properties. If a request does not provide its own transformations
8906
     * then these will be applied.
8907
     *
8908
     * You can augment or replace the default transformations by modifying these properties by adding to or
8909
     * replacing the array.
8910
     *
8911
     * Angular provides the following default transformations:
8912
     *
8913
     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
8914
     *
8915
     * - If the `data` property of the request configuration object contains an object, serialize it
8916
     *   into JSON format.
8917
     *
8918
     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
8919
     *
8920
     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
8921
     *  - If JSON response is detected, deserialize it using a JSON parser.
8922
     *
8923
     *
8924
     * ### Overriding the Default Transformations Per Request
8925
     *
8926
     * If you wish override the request/response transformations only for a single request then provide
8927
     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
8928
     * into `$http`.
8929
     *
8930
     * Note that if you provide these properties on the config object the default transformations will be
8931
     * overwritten. If you wish to augment the default transformations then you must include them in your
8932
     * local transformation array.
8933
     *
8934
     * The following code demonstrates adding a new response transformation to be run after the default response
8935
     * transformations have been run.
8936
     *
8937
     * ```js
8938
     * function appendTransform(defaults, transform) {
8939
     *
8940
     *   // We can't guarantee that the default transformation is an array
8941
     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
8942
     *
8943
     *   // Append the new transformation to the defaults
8944
     *   return defaults.concat(transform);
8945
     * }
8946
     *
8947
     * $http({
8948
     *   url: '...',
8949
     *   method: 'GET',
8950
     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
8951
     *     return doTransform(value);
8952
     *   })
8953
     * });
8954
     * ```
8955
     *
8956
     *
8957
     * ## Caching
8958
     *
8959
     * To enable caching, set the request configuration `cache` property to `true` (to use default
8960
     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
8961
     * When the cache is enabled, `$http` stores the response from the server in the specified
8962
     * cache. The next time the same request is made, the response is served from the cache without
8963
     * sending a request to the server.
8964
     *
8965
     * Note that even if the response is served from cache, delivery of the data is asynchronous in
8966
     * the same way that real requests are.
8967
     *
8968
     * If there are multiple GET requests for the same URL that should be cached using the same
8969
     * cache, but the cache is not populated yet, only one request to the server will be made and
8970
     * the remaining requests will be fulfilled using the response from the first request.
8971
     *
8972
     * You can change the default cache to a new object (built with
8973
     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
8974
     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
8975
     * their `cache` property to `true` will now use this cache object.
8976
     *
8977
     * If you set the default cache to `false` then only requests that specify their own custom
8978
     * cache object will be cached.
8979
     *
8980
     * ## Interceptors
8981
     *
8982
     * Before you start creating interceptors, be sure to understand the
8983
     * {@link ng.$q $q and deferred/promise APIs}.
8984
     *
8985
     * For purposes of global error handling, authentication, or any kind of synchronous or
8986
     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
8987
     * able to intercept requests before they are handed to the server and
8988
     * responses before they are handed over to the application code that
8989
     * initiated these requests. The interceptors leverage the {@link ng.$q
8990
     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
8991
     *
8992
     * The interceptors are service factories that are registered with the `$httpProvider` by
8993
     * adding them to the `$httpProvider.interceptors` array. The factory is called and
8994
     * injected with dependencies (if specified) and returns the interceptor.
8995
     *
8996
     * There are two kinds of interceptors (and two kinds of rejection interceptors):
8997
     *
8998
     *   * `request`: interceptors get called with a http `config` object. The function is free to
8999
     *     modify the `config` object or create a new one. The function needs to return the `config`
9000
     *     object directly, or a promise containing the `config` or a new `config` object.
9001
     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
9002
     *     resolved with a rejection.
9003
     *   * `response`: interceptors get called with http `response` object. The function is free to
9004
     *     modify the `response` object or create a new one. The function needs to return the `response`
9005
     *     object directly, or as a promise containing the `response` or a new `response` object.
9006
     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
9007
     *     resolved with a rejection.
9008
     *
9009
     *
9010
     * ```js
9011
     *   // register the interceptor as a service
9012
     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9013
     *     return {
9014
     *       // optional method
9015
     *       'request': function(config) {
9016
     *         // do something on success
9017
     *         return config;
9018
     *       },
9019
     *
9020
     *       // optional method
9021
     *      'requestError': function(rejection) {
9022
     *         // do something on error
9023
     *         if (canRecover(rejection)) {
9024
     *           return responseOrNewPromise
9025
     *         }
9026
     *         return $q.reject(rejection);
9027
     *       },
9028
     *
9029
     *
9030
     *
9031
     *       // optional method
9032
     *       'response': function(response) {
9033
     *         // do something on success
9034
     *         return response;
9035
     *       },
9036
     *
9037
     *       // optional method
9038
     *      'responseError': function(rejection) {
9039
     *         // do something on error
9040
     *         if (canRecover(rejection)) {
9041
     *           return responseOrNewPromise
9042
     *         }
9043
     *         return $q.reject(rejection);
9044
     *       }
9045
     *     };
9046
     *   });
9047
     *
9048
     *   $httpProvider.interceptors.push('myHttpInterceptor');
9049
     *
9050
     *
9051
     *   // alternatively, register the interceptor via an anonymous factory
9052
     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9053
     *     return {
9054
     *      'request': function(config) {
9055
     *          // same as above
9056
     *       },
9057
     *
9058
     *       'response': function(response) {
9059
     *          // same as above
9060
     *       }
9061
     *     };
9062
     *   });
9063
     * ```
9064
     *
9065
     * ## Security Considerations
9066
     *
9067
     * When designing web applications, consider security threats from:
9068
     *
9069
     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9070
     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9071
     *
9072
     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9073
     * pre-configured with strategies that address these issues, but for this to work backend server
9074
     * cooperation is required.
9075
     *
9076
     * ### JSON Vulnerability Protection
9077
     *
9078
     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9079
     * allows third party website to turn your JSON resource URL into
9080
     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9081
     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9082
     * Angular will automatically strip the prefix before processing it as JSON.
9083
     *
9084
     * For example if your server needs to return:
9085
     * ```js
9086
     * ['one','two']
9087
     * ```
9088
     *
9089
     * which is vulnerable to attack, your server can return:
9090
     * ```js
9091
     * )]}',
9092
     * ['one','two']
9093
     * ```
9094
     *
9095
     * Angular will strip the prefix, before processing the JSON.
9096
     *
9097
     *
9098
     * ### Cross Site Request Forgery (XSRF) Protection
9099
     *
9100
     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9101
     * an unauthorized site can gain your user's private data. Angular provides a mechanism
9102
     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9103
     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9104
     * JavaScript that runs on your domain could read the cookie, your server can be assured that
9105
     * the XHR came from JavaScript running on your domain. The header will not be set for
9106
     * cross-domain requests.
9107
     *
9108
     * To take advantage of this, your server needs to set a token in a JavaScript readable session
9109
     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9110
     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9111
     * that only JavaScript running on your domain could have sent the request. The token must be
9112
     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9113
     * making up its own tokens). We recommend that the token is a digest of your site's
9114
     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
9115
     * for added security.
9116
     *
9117
     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9118
     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9119
     * or the per-request config object.
9120
     *
9121
     *
9122
     * @param {object} config Object describing the request to be made and how it should be
9123
     *    processed. The object has following properties:
9124
     *
9125
     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9126
     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9127
     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
9128
     *      to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
9129
     *      JSONified.
9130
     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
9131
     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
9132
     *      HTTP headers to send to the server. If the return value of a function is null, the
9133
     *      header will not be sent.
9134
     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9135
     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9136
     *    - **transformRequest** –
9137
     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9138
     *      transform function or an array of such functions. The transform function takes the http
9139
     *      request body and headers and returns its transformed (typically serialized) version.
9140
     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9141
     *      Overriding the Default Transformations}
9142
     *    - **transformResponse** –
9143
     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9144
     *      transform function or an array of such functions. The transform function takes the http
9145
     *      response body and headers and returns its transformed (typically deserialized) version.
9146
     *      See {@link ng.$http#overriding-the-default-transformations-per-request
9147
     *      Overriding the Default Transformations}
9148
     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9149
     *      GET request, otherwise if a cache instance built with
9150
     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9151
     *      caching.
9152
     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9153
     *      that should abort the request when resolved.
9154
     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9155
     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9156
     *      for more information.
9157
     *    - **responseType** - `{string}` - see
9158
     *      [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9159
     *
9160
     * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9161
     *   standard `then` method and two http specific methods: `success` and `error`. The `then`
9162
     *   method takes two arguments a success and an error callback which will be called with a
9163
     *   response object. The `success` and `error` methods take a single argument - a function that
9164
     *   will be called when the request succeeds or fails respectively. The arguments passed into
9165
     *   these functions are destructured representation of the response object passed into the
9166
     *   `then` method. The response object has these properties:
9167
     *
9168
     *   - **data** – `{string|Object}` – The response body transformed with the transform
9169
     *     functions.
9170
     *   - **status** – `{number}` – HTTP status code of the response.
9171
     *   - **headers** – `{function([headerName])}` – Header getter function.
9172
     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9173
     *   - **statusText** – `{string}` – HTTP status text of the response.
9174
     *
9175
     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9176
     *   requests. This is primarily meant to be used for debugging purposes.
9177
     *
9178
     *
9179
     * @example
9180
<example module="httpExample">
9181
<file name="index.html">
9182
  <div ng-controller="FetchController">
9183
    <select ng-model="method">
9184
      <option>GET</option>
9185
      <option>JSONP</option>
9186
    </select>
9187
    <input type="text" ng-model="url" size="80"/>
9188
    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9189
    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9190
    <button id="samplejsonpbtn"
9191
      ng-click="updateModel('JSONP',
9192
                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9193
      Sample JSONP
9194
    </button>
9195
    <button id="invalidjsonpbtn"
9196
      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9197
        Invalid JSONP
9198
      </button>
9199
    <pre>http status code: {{status}}</pre>
9200
    <pre>http response data: {{data}}</pre>
9201
  </div>
9202
</file>
9203
<file name="script.js">
9204
  angular.module('httpExample', [])
9205
    .controller('FetchController', ['$scope', '$http', '$templateCache',
9206
      function($scope, $http, $templateCache) {
9207
        $scope.method = 'GET';
9208
        $scope.url = 'http-hello.html';
9209
 
9210
        $scope.fetch = function() {
9211
          $scope.code = null;
9212
          $scope.response = null;
9213
 
9214
          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9215
            success(function(data, status) {
9216
              $scope.status = status;
9217
              $scope.data = data;
9218
            }).
9219
            error(function(data, status) {
9220
              $scope.data = data || "Request failed";
9221
              $scope.status = status;
9222
          });
9223
        };
9224
 
9225
        $scope.updateModel = function(method, url) {
9226
          $scope.method = method;
9227
          $scope.url = url;
9228
        };
9229
      }]);
9230
</file>
9231
<file name="http-hello.html">
9232
  Hello, $http!
9233
</file>
9234
<file name="protractor.js" type="protractor">
9235
  var status = element(by.binding('status'));
9236
  var data = element(by.binding('data'));
9237
  var fetchBtn = element(by.id('fetchbtn'));
9238
  var sampleGetBtn = element(by.id('samplegetbtn'));
9239
  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9240
  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9241
 
9242
  it('should make an xhr GET request', function() {
9243
    sampleGetBtn.click();
9244
    fetchBtn.click();
9245
    expect(status.getText()).toMatch('200');
9246
    expect(data.getText()).toMatch(/Hello, \$http!/);
9247
  });
9248
 
9249
// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9250
// it('should make a JSONP request to angularjs.org', function() {
9251
//   sampleJsonpBtn.click();
9252
//   fetchBtn.click();
9253
//   expect(status.getText()).toMatch('200');
9254
//   expect(data.getText()).toMatch(/Super Hero!/);
9255
// });
9256
 
9257
  it('should make JSONP request to invalid URL and invoke the error handler',
9258
      function() {
9259
    invalidJsonpBtn.click();
9260
    fetchBtn.click();
9261
    expect(status.getText()).toMatch('0');
9262
    expect(data.getText()).toMatch('Request failed');
9263
  });
9264
</file>
9265
</example>
9266
     */
9267
    function $http(requestConfig) {
9268
      var config = {
9269
        method: 'get',
9270
        transformRequest: defaults.transformRequest,
9271
        transformResponse: defaults.transformResponse
9272
      };
9273
      var headers = mergeHeaders(requestConfig);
9274
 
9275
      if (!angular.isObject(requestConfig)) {
9276
        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
9277
      }
9278
 
9279
      extend(config, requestConfig);
9280
      config.headers = headers;
9281
      config.method = uppercase(config.method);
9282
 
9283
      var serverRequest = function(config) {
9284
        headers = config.headers;
9285
        var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
9286
 
9287
        // strip content-type if data is undefined
9288
        if (isUndefined(reqData)) {
9289
          forEach(headers, function(value, header) {
9290
            if (lowercase(header) === 'content-type') {
9291
                delete headers[header];
9292
            }
9293
          });
9294
        }
9295
 
9296
        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9297
          config.withCredentials = defaults.withCredentials;
9298
        }
9299
 
9300
        // send request
9301
        return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
9302
      };
9303
 
9304
      var chain = [serverRequest, undefined];
9305
      var promise = $q.when(config);
9306
 
9307
      // apply interceptors
9308
      forEach(reversedInterceptors, function(interceptor) {
9309
        if (interceptor.request || interceptor.requestError) {
9310
          chain.unshift(interceptor.request, interceptor.requestError);
9311
        }
9312
        if (interceptor.response || interceptor.responseError) {
9313
          chain.push(interceptor.response, interceptor.responseError);
9314
        }
9315
      });
9316
 
9317
      while (chain.length) {
9318
        var thenFn = chain.shift();
9319
        var rejectFn = chain.shift();
9320
 
9321
        promise = promise.then(thenFn, rejectFn);
9322
      }
9323
 
9324
      promise.success = function(fn) {
9325
        promise.then(function(response) {
9326
          fn(response.data, response.status, response.headers, config);
9327
        });
9328
        return promise;
9329
      };
9330
 
9331
      promise.error = function(fn) {
9332
        promise.then(null, function(response) {
9333
          fn(response.data, response.status, response.headers, config);
9334
        });
9335
        return promise;
9336
      };
9337
 
9338
      return promise;
9339
 
9340
      function transformResponse(response) {
9341
        // make a copy since the response must be cacheable
9342
        var resp = extend({}, response);
9343
        if (!response.data) {
9344
          resp.data = response.data;
9345
        } else {
9346
          resp.data = transformData(response.data, response.headers, config.transformResponse);
9347
        }
9348
        return (isSuccess(response.status))
9349
          ? resp
9350
          : $q.reject(resp);
9351
      }
9352
 
9353
      function mergeHeaders(config) {
9354
        var defHeaders = defaults.headers,
9355
            reqHeaders = extend({}, config.headers),
9356
            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
9357
 
9358
        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
9359
 
9360
        // using for-in instead of forEach to avoid unecessary iteration after header has been found
9361
        defaultHeadersIteration:
9362
        for (defHeaderName in defHeaders) {
9363
          lowercaseDefHeaderName = lowercase(defHeaderName);
9364
 
9365
          for (reqHeaderName in reqHeaders) {
9366
            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
9367
              continue defaultHeadersIteration;
9368
            }
9369
          }
9370
 
9371
          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
9372
        }
9373
 
9374
        // execute if header value is a function for merged headers
9375
        execHeaders(reqHeaders);
9376
        return reqHeaders;
9377
 
9378
        function execHeaders(headers) {
9379
          var headerContent;
9380
 
9381
          forEach(headers, function(headerFn, header) {
9382
            if (isFunction(headerFn)) {
9383
              headerContent = headerFn();
9384
              if (headerContent != null) {
9385
                headers[header] = headerContent;
9386
              } else {
9387
                delete headers[header];
9388
              }
9389
            }
9390
          });
9391
        }
9392
      }
9393
    }
9394
 
9395
    $http.pendingRequests = [];
9396
 
9397
    /**
9398
     * @ngdoc method
9399
     * @name $http#get
9400
     *
9401
     * @description
9402
     * Shortcut method to perform `GET` request.
9403
     *
9404
     * @param {string} url Relative or absolute URL specifying the destination of the request
9405
     * @param {Object=} config Optional configuration object
9406
     * @returns {HttpPromise} Future object
9407
     */
9408
 
9409
    /**
9410
     * @ngdoc method
9411
     * @name $http#delete
9412
     *
9413
     * @description
9414
     * Shortcut method to perform `DELETE` request.
9415
     *
9416
     * @param {string} url Relative or absolute URL specifying the destination of the request
9417
     * @param {Object=} config Optional configuration object
9418
     * @returns {HttpPromise} Future object
9419
     */
9420
 
9421
    /**
9422
     * @ngdoc method
9423
     * @name $http#head
9424
     *
9425
     * @description
9426
     * Shortcut method to perform `HEAD` request.
9427
     *
9428
     * @param {string} url Relative or absolute URL specifying the destination of the request
9429
     * @param {Object=} config Optional configuration object
9430
     * @returns {HttpPromise} Future object
9431
     */
9432
 
9433
    /**
9434
     * @ngdoc method
9435
     * @name $http#jsonp
9436
     *
9437
     * @description
9438
     * Shortcut method to perform `JSONP` request.
9439
     *
9440
     * @param {string} url Relative or absolute URL specifying the destination of the request.
9441
     *                     The name of the callback should be the string `JSON_CALLBACK`.
9442
     * @param {Object=} config Optional configuration object
9443
     * @returns {HttpPromise} Future object
9444
     */
9445
    createShortMethods('get', 'delete', 'head', 'jsonp');
9446
 
9447
    /**
9448
     * @ngdoc method
9449
     * @name $http#post
9450
     *
9451
     * @description
9452
     * Shortcut method to perform `POST` request.
9453
     *
9454
     * @param {string} url Relative or absolute URL specifying the destination of the request
9455
     * @param {*} data Request content
9456
     * @param {Object=} config Optional configuration object
9457
     * @returns {HttpPromise} Future object
9458
     */
9459
 
9460
    /**
9461
     * @ngdoc method
9462
     * @name $http#put
9463
     *
9464
     * @description
9465
     * Shortcut method to perform `PUT` request.
9466
     *
9467
     * @param {string} url Relative or absolute URL specifying the destination of the request
9468
     * @param {*} data Request content
9469
     * @param {Object=} config Optional configuration object
9470
     * @returns {HttpPromise} Future object
9471
     */
9472
 
9473
     /**
9474
      * @ngdoc method
9475
      * @name $http#patch
9476
      *
9477
      * @description
9478
      * Shortcut method to perform `PATCH` request.
9479
      *
9480
      * @param {string} url Relative or absolute URL specifying the destination of the request
9481
      * @param {*} data Request content
9482
      * @param {Object=} config Optional configuration object
9483
      * @returns {HttpPromise} Future object
9484
      */
9485
    createShortMethodsWithData('post', 'put', 'patch');
9486
 
9487
        /**
9488
         * @ngdoc property
9489
         * @name $http#defaults
9490
         *
9491
         * @description
9492
         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
9493
         * default headers, withCredentials as well as request and response transformations.
9494
         *
9495
         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
9496
         */
9497
    $http.defaults = defaults;
9498
 
9499
 
9500
    return $http;
9501
 
9502
 
9503
    function createShortMethods(names) {
9504
      forEach(arguments, function(name) {
9505
        $http[name] = function(url, config) {
9506
          return $http(extend(config || {}, {
9507
            method: name,
9508
            url: url
9509
          }));
9510
        };
9511
      });
9512
    }
9513
 
9514
 
9515
    function createShortMethodsWithData(name) {
9516
      forEach(arguments, function(name) {
9517
        $http[name] = function(url, data, config) {
9518
          return $http(extend(config || {}, {
9519
            method: name,
9520
            url: url,
9521
            data: data
9522
          }));
9523
        };
9524
      });
9525
    }
9526
 
9527
 
9528
    /**
9529
     * Makes the request.
9530
     *
9531
     * !!! ACCESSES CLOSURE VARS:
9532
     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9533
     */
9534
    function sendReq(config, reqData, reqHeaders) {
9535
      var deferred = $q.defer(),
9536
          promise = deferred.promise,
9537
          cache,
9538
          cachedResp,
9539
          url = buildUrl(config.url, config.params);
9540
 
9541
      $http.pendingRequests.push(config);
9542
      promise.then(removePendingReq, removePendingReq);
9543
 
9544
 
9545
      if ((config.cache || defaults.cache) && config.cache !== false &&
9546
          (config.method === 'GET' || config.method === 'JSONP')) {
9547
        cache = isObject(config.cache) ? config.cache
9548
              : isObject(defaults.cache) ? defaults.cache
9549
              : defaultCache;
9550
      }
9551
 
9552
      if (cache) {
9553
        cachedResp = cache.get(url);
9554
        if (isDefined(cachedResp)) {
9555
          if (isPromiseLike(cachedResp)) {
9556
            // cached request has already been sent, but there is no response yet
9557
            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
9558
          } else {
9559
            // serving from cache
9560
            if (isArray(cachedResp)) {
9561
              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
9562
            } else {
9563
              resolvePromise(cachedResp, 200, {}, 'OK');
9564
            }
9565
          }
9566
        } else {
9567
          // put the promise for the non-transformed response into cache as a placeholder
9568
          cache.put(url, promise);
9569
        }
9570
      }
9571
 
9572
 
9573
      // if we won't have the response in cache, set the xsrf headers and
9574
      // send the request to the backend
9575
      if (isUndefined(cachedResp)) {
9576
        var xsrfValue = urlIsSameOrigin(config.url)
9577
            ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
9578
            : undefined;
9579
        if (xsrfValue) {
9580
          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
9581
        }
9582
 
9583
        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9584
            config.withCredentials, config.responseType);
9585
      }
9586
 
9587
      return promise;
9588
 
9589
 
9590
      /**
9591
       * Callback registered to $httpBackend():
9592
       *  - caches the response if desired
9593
       *  - resolves the raw $http promise
9594
       *  - calls $apply
9595
       */
9596
      function done(status, response, headersString, statusText) {
9597
        if (cache) {
9598
          if (isSuccess(status)) {
9599
            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
9600
          } else {
9601
            // remove promise from the cache
9602
            cache.remove(url);
9603
          }
9604
        }
9605
 
9606
        function resolveHttpPromise() {
9607
          resolvePromise(response, status, headersString, statusText);
9608
        }
9609
 
9610
        if (useApplyAsync) {
9611
          $rootScope.$applyAsync(resolveHttpPromise);
9612
        } else {
9613
          resolveHttpPromise();
9614
          if (!$rootScope.$$phase) $rootScope.$apply();
9615
        }
9616
      }
9617
 
9618
 
9619
      /**
9620
       * Resolves the raw $http promise.
9621
       */
9622
      function resolvePromise(response, status, headers, statusText) {
9623
        // normalize internal statuses to 0
9624
        status = Math.max(status, 0);
9625
 
9626
        (isSuccess(status) ? deferred.resolve : deferred.reject)({
9627
          data: response,
9628
          status: status,
9629
          headers: headersGetter(headers),
9630
          config: config,
9631
          statusText: statusText
9632
        });
9633
      }
9634
 
9635
      function resolvePromiseWithResult(result) {
9636
        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
9637
      }
9638
 
9639
      function removePendingReq() {
9640
        var idx = $http.pendingRequests.indexOf(config);
9641
        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
9642
      }
9643
    }
9644
 
9645
 
9646
    function buildUrl(url, params) {
9647
      if (!params) return url;
9648
      var parts = [];
9649
      forEachSorted(params, function(value, key) {
9650
        if (value === null || isUndefined(value)) return;
9651
        if (!isArray(value)) value = [value];
9652
 
9653
        forEach(value, function(v) {
9654
          if (isObject(v)) {
9655
            if (isDate(v)) {
9656
              v = v.toISOString();
9657
            } else {
9658
              v = toJson(v);
9659
            }
9660
          }
9661
          parts.push(encodeUriQuery(key) + '=' +
9662
                     encodeUriQuery(v));
9663
        });
9664
      });
9665
      if (parts.length > 0) {
9666
        url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9667
      }
9668
      return url;
9669
    }
9670
  }];
9671
}
9672
 
9673
function createXhr() {
9674
    return new window.XMLHttpRequest();
9675
}
9676
 
9677
/**
9678
 * @ngdoc service
9679
 * @name $httpBackend
9680
 * @requires $window
9681
 * @requires $document
9682
 *
9683
 * @description
9684
 * HTTP backend used by the {@link ng.$http service} that delegates to
9685
 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
9686
 *
9687
 * You should never need to use this service directly, instead use the higher-level abstractions:
9688
 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
9689
 *
9690
 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
9691
 * $httpBackend} which can be trained with responses.
9692
 */
9693
function $HttpBackendProvider() {
9694
  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
9695
    return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
9696
  }];
9697
}
9698
 
9699
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
9700
  // TODO(vojta): fix the signature
9701
  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9702
    $browser.$$incOutstandingRequestCount();
9703
    url = url || $browser.url();
9704
 
9705
    if (lowercase(method) == 'jsonp') {
9706
      var callbackId = '_' + (callbacks.counter++).toString(36);
9707
      callbacks[callbackId] = function(data) {
9708
        callbacks[callbackId].data = data;
9709
        callbacks[callbackId].called = true;
9710
      };
9711
 
9712
      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
9713
          callbackId, function(status, text) {
9714
        completeRequest(callback, status, callbacks[callbackId].data, "", text);
9715
        callbacks[callbackId] = noop;
9716
      });
9717
    } else {
9718
 
9719
      var xhr = createXhr();
9720
 
9721
      xhr.open(method, url, true);
9722
      forEach(headers, function(value, key) {
9723
        if (isDefined(value)) {
9724
            xhr.setRequestHeader(key, value);
9725
        }
9726
      });
9727
 
9728
      xhr.onload = function requestLoaded() {
9729
        var statusText = xhr.statusText || '';
9730
 
9731
        // responseText is the old-school way of retrieving response (supported by IE8 & 9)
9732
        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
9733
        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
9734
 
9735
        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
9736
        var status = xhr.status === 1223 ? 204 : xhr.status;
9737
 
9738
        // fix status code when it is 0 (0 status is undocumented).
9739
        // Occurs when accessing file resources or on Android 4.1 stock browser
9740
        // while retrieving files from application cache.
9741
        if (status === 0) {
9742
          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
9743
        }
9744
 
9745
        completeRequest(callback,
9746
            status,
9747
            response,
9748
            xhr.getAllResponseHeaders(),
9749
            statusText);
9750
      };
9751
 
9752
      var requestError = function() {
9753
        // The response is always empty
9754
        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
9755
        completeRequest(callback, -1, null, null, '');
9756
      };
9757
 
9758
      xhr.onerror = requestError;
9759
      xhr.onabort = requestError;
9760
 
9761
      if (withCredentials) {
9762
        xhr.withCredentials = true;
9763
      }
9764
 
9765
      if (responseType) {
9766
        try {
9767
          xhr.responseType = responseType;
9768
        } catch (e) {
9769
          // WebKit added support for the json responseType value on 09/03/2013
9770
          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
9771
          // known to throw when setting the value "json" as the response type. Other older
9772
          // browsers implementing the responseType
9773
          //
9774
          // The json response type can be ignored if not supported, because JSON payloads are
9775
          // parsed on the client-side regardless.
9776
          if (responseType !== 'json') {
9777
            throw e;
9778
          }
9779
        }
9780
      }
9781
 
9782
      xhr.send(post || null);
9783
    }
9784
 
9785
    if (timeout > 0) {
9786
      var timeoutId = $browserDefer(timeoutRequest, timeout);
9787
    } else if (isPromiseLike(timeout)) {
9788
      timeout.then(timeoutRequest);
9789
    }
9790
 
9791
 
9792
    function timeoutRequest() {
9793
      jsonpDone && jsonpDone();
9794
      xhr && xhr.abort();
9795
    }
9796
 
9797
    function completeRequest(callback, status, response, headersString, statusText) {
9798
      // cancel timeout and subsequent timeout promise resolution
9799
      if (timeoutId !== undefined) {
9800
        $browserDefer.cancel(timeoutId);
9801
      }
9802
      jsonpDone = xhr = null;
9803
 
9804
      callback(status, response, headersString, statusText);
9805
      $browser.$$completeOutstandingRequest(noop);
9806
    }
9807
  };
9808
 
9809
  function jsonpReq(url, callbackId, done) {
9810
    // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
9811
    // - fetches local scripts via XHR and evals them
9812
    // - adds and immediately removes script elements from the document
9813
    var script = rawDocument.createElement('script'), callback = null;
9814
    script.type = "text/javascript";
9815
    script.src = url;
9816
    script.async = true;
9817
 
9818
    callback = function(event) {
9819
      removeEventListenerFn(script, "load", callback);
9820
      removeEventListenerFn(script, "error", callback);
9821
      rawDocument.body.removeChild(script);
9822
      script = null;
9823
      var status = -1;
9824
      var text = "unknown";
9825
 
9826
      if (event) {
9827
        if (event.type === "load" && !callbacks[callbackId].called) {
9828
          event = { type: "error" };
9829
        }
9830
        text = event.type;
9831
        status = event.type === "error" ? 404 : 200;
9832
      }
9833
 
9834
      if (done) {
9835
        done(status, text);
9836
      }
9837
    };
9838
 
9839
    addEventListenerFn(script, "load", callback);
9840
    addEventListenerFn(script, "error", callback);
9841
    rawDocument.body.appendChild(script);
9842
    return callback;
9843
  }
9844
}
9845
 
9846
var $interpolateMinErr = minErr('$interpolate');
9847
 
9848
/**
9849
 * @ngdoc provider
9850
 * @name $interpolateProvider
9851
 *
9852
 * @description
9853
 *
9854
 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
9855
 *
9856
 * @example
9857
<example module="customInterpolationApp">
9858
<file name="index.html">
9859
<script>
9860
  var customInterpolationApp = angular.module('customInterpolationApp', []);
9861
 
9862
  customInterpolationApp.config(function($interpolateProvider) {
9863
    $interpolateProvider.startSymbol('//');
9864
    $interpolateProvider.endSymbol('//');
9865
  });
9866
 
9867
 
9868
  customInterpolationApp.controller('DemoController', function() {
9869
      this.label = "This binding is brought you by // interpolation symbols.";
9870
  });
9871
</script>
9872
<div ng-app="App" ng-controller="DemoController as demo">
9873
    //demo.label//
9874
</div>
9875
</file>
9876
<file name="protractor.js" type="protractor">
9877
  it('should interpolate binding with custom symbols', function() {
9878
    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
9879
  });
9880
</file>
9881
</example>
9882
 */
9883
function $InterpolateProvider() {
9884
  var startSymbol = '{{';
9885
  var endSymbol = '}}';
9886
 
9887
  /**
9888
   * @ngdoc method
9889
   * @name $interpolateProvider#startSymbol
9890
   * @description
9891
   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
9892
   *
9893
   * @param {string=} value new value to set the starting symbol to.
9894
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9895
   */
9896
  this.startSymbol = function(value) {
9897
    if (value) {
9898
      startSymbol = value;
9899
      return this;
9900
    } else {
9901
      return startSymbol;
9902
    }
9903
  };
9904
 
9905
  /**
9906
   * @ngdoc method
9907
   * @name $interpolateProvider#endSymbol
9908
   * @description
9909
   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
9910
   *
9911
   * @param {string=} value new value to set the ending symbol to.
9912
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9913
   */
9914
  this.endSymbol = function(value) {
9915
    if (value) {
9916
      endSymbol = value;
9917
      return this;
9918
    } else {
9919
      return endSymbol;
9920
    }
9921
  };
9922
 
9923
 
9924
  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
9925
    var startSymbolLength = startSymbol.length,
9926
        endSymbolLength = endSymbol.length,
9927
        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
9928
        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
9929
 
9930
    function escape(ch) {
9931
      return '\\\\\\' + ch;
9932
    }
9933
 
9934
    /**
9935
     * @ngdoc service
9936
     * @name $interpolate
9937
     * @kind function
9938
     *
9939
     * @requires $parse
9940
     * @requires $sce
9941
     *
9942
     * @description
9943
     *
9944
     * Compiles a string with markup into an interpolation function. This service is used by the
9945
     * HTML {@link ng.$compile $compile} service for data binding. See
9946
     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
9947
     * interpolation markup.
9948
     *
9949
     *
9950
     * ```js
9951
     *   var $interpolate = ...; // injected
9952
     *   var exp = $interpolate('Hello {{name | uppercase}}!');
9953
     *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
9954
     * ```
9955
     *
9956
     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
9957
     * `true`, the interpolation function will return `undefined` unless all embedded expressions
9958
     * evaluate to a value other than `undefined`.
9959
     *
9960
     * ```js
9961
     *   var $interpolate = ...; // injected
9962
     *   var context = {greeting: 'Hello', name: undefined };
9963
     *
9964
     *   // default "forgiving" mode
9965
     *   var exp = $interpolate('{{greeting}} {{name}}!');
9966
     *   expect(exp(context)).toEqual('Hello !');
9967
     *
9968
     *   // "allOrNothing" mode
9969
     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
9970
     *   expect(exp(context)).toBeUndefined();
9971
     *   context.name = 'Angular';
9972
     *   expect(exp(context)).toEqual('Hello Angular!');
9973
     * ```
9974
     *
9975
     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
9976
     *
9977
     * ####Escaped Interpolation
9978
     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
9979
     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
9980
     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
9981
     * or binding.
9982
     *
9983
     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
9984
     * degree, while also enabling code examples to work without relying on the
9985
     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
9986
     *
9987
     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
9988
     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
9989
     * interpolation start/end markers with their escaped counterparts.**
9990
     *
9991
     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
9992
     * output when the $interpolate service processes the text. So, for HTML elements interpolated
9993
     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
9994
     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
9995
     * this is typically useful only when user-data is used in rendering a template from the server, or
9996
     * when otherwise untrusted data is used by a directive.
9997
     *
9998
     * <example>
9999
     *  <file name="index.html">
10000
     *    <div ng-init="username='A user'">
10001
     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10002
     *        </p>
10003
     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
10004
     *        application, but fails to accomplish their task, because the server has correctly
10005
     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10006
     *        characters.</p>
10007
     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
10008
     *        from the database by an administrator.</p>
10009
     *    </div>
10010
     *  </file>
10011
     * </example>
10012
     *
10013
     * @param {string} text The text with markup to interpolate.
10014
     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10015
     *    embedded expression in order to return an interpolation function. Strings with no
10016
     *    embedded expression will return null for the interpolation function.
10017
     * @param {string=} trustedContext when provided, the returned function passes the interpolated
10018
     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10019
     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
10020
     *    provides Strict Contextual Escaping for details.
10021
     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10022
     *    unless all embedded expressions evaluate to a value other than `undefined`.
10023
     * @returns {function(context)} an interpolation function which is used to compute the
10024
     *    interpolated string. The function has these parameters:
10025
     *
10026
     * - `context`: evaluation context for all expressions embedded in the interpolated text
10027
     */
10028
    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
10029
      allOrNothing = !!allOrNothing;
10030
      var startIndex,
10031
          endIndex,
10032
          index = 0,
10033
          expressions = [],
10034
          parseFns = [],
10035
          textLength = text.length,
10036
          exp,
10037
          concat = [],
10038
          expressionPositions = [];
10039
 
10040
      while (index < textLength) {
10041
        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
10042
             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
10043
          if (index !== startIndex) {
10044
            concat.push(unescapeText(text.substring(index, startIndex)));
10045
          }
10046
          exp = text.substring(startIndex + startSymbolLength, endIndex);
10047
          expressions.push(exp);
10048
          parseFns.push($parse(exp, parseStringifyInterceptor));
10049
          index = endIndex + endSymbolLength;
10050
          expressionPositions.push(concat.length);
10051
          concat.push('');
10052
        } else {
10053
          // we did not find an interpolation, so we have to add the remainder to the separators array
10054
          if (index !== textLength) {
10055
            concat.push(unescapeText(text.substring(index)));
10056
          }
10057
          break;
10058
        }
10059
      }
10060
 
10061
      // Concatenating expressions makes it hard to reason about whether some combination of
10062
      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
10063
      // single expression be used for iframe[src], object[src], etc., we ensure that the value
10064
      // that's used is assigned or constructed by some JS code somewhere that is more testable or
10065
      // make it obvious that you bound the value to some user controlled value.  This helps reduce
10066
      // the load when auditing for XSS issues.
10067
      if (trustedContext && concat.length > 1) {
10068
          throw $interpolateMinErr('noconcat',
10069
              "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10070
              "interpolations that concatenate multiple expressions when a trusted value is " +
10071
              "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10072
      }
10073
 
10074
      if (!mustHaveExpression || expressions.length) {
10075
        var compute = function(values) {
10076
          for (var i = 0, ii = expressions.length; i < ii; i++) {
10077
            if (allOrNothing && isUndefined(values[i])) return;
10078
            concat[expressionPositions[i]] = values[i];
10079
          }
10080
          return concat.join('');
10081
        };
10082
 
10083
        var getValue = function(value) {
10084
          return trustedContext ?
10085
            $sce.getTrusted(trustedContext, value) :
10086
            $sce.valueOf(value);
10087
        };
10088
 
10089
        var stringify = function(value) {
10090
          if (value == null) { // null || undefined
10091
            return '';
10092
          }
10093
          switch (typeof value) {
10094
            case 'string':
10095
              break;
10096
            case 'number':
10097
              value = '' + value;
10098
              break;
10099
            default:
10100
              value = toJson(value);
10101
          }
10102
 
10103
          return value;
10104
        };
10105
 
10106
        return extend(function interpolationFn(context) {
10107
            var i = 0;
10108
            var ii = expressions.length;
10109
            var values = new Array(ii);
10110
 
10111
            try {
10112
              for (; i < ii; i++) {
10113
                values[i] = parseFns[i](context);
10114
              }
10115
 
10116
              return compute(values);
10117
            } catch (err) {
10118
              var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10119
                  err.toString());
10120
              $exceptionHandler(newErr);
10121
            }
10122
 
10123
          }, {
10124
          // all of these properties are undocumented for now
10125
          exp: text, //just for compatibility with regular watchers created via $watch
10126
          expressions: expressions,
10127
          $$watchDelegate: function(scope, listener, objectEquality) {
10128
            var lastValue;
10129
            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10130
              var currValue = compute(values);
10131
              if (isFunction(listener)) {
10132
                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10133
              }
10134
              lastValue = currValue;
10135
            }, objectEquality);
10136
          }
10137
        });
10138
      }
10139
 
10140
      function unescapeText(text) {
10141
        return text.replace(escapedStartRegexp, startSymbol).
10142
          replace(escapedEndRegexp, endSymbol);
10143
      }
10144
 
10145
      function parseStringifyInterceptor(value) {
10146
        try {
10147
          value = getValue(value);
10148
          return allOrNothing && !isDefined(value) ? value : stringify(value);
10149
        } catch (err) {
10150
          var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10151
            err.toString());
10152
          $exceptionHandler(newErr);
10153
        }
10154
      }
10155
    }
10156
 
10157
 
10158
    /**
10159
     * @ngdoc method
10160
     * @name $interpolate#startSymbol
10161
     * @description
10162
     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10163
     *
10164
     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10165
     * the symbol.
10166
     *
10167
     * @returns {string} start symbol.
10168
     */
10169
    $interpolate.startSymbol = function() {
10170
      return startSymbol;
10171
    };
10172
 
10173
 
10174
    /**
10175
     * @ngdoc method
10176
     * @name $interpolate#endSymbol
10177
     * @description
10178
     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10179
     *
10180
     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10181
     * the symbol.
10182
     *
10183
     * @returns {string} end symbol.
10184
     */
10185
    $interpolate.endSymbol = function() {
10186
      return endSymbol;
10187
    };
10188
 
10189
    return $interpolate;
10190
  }];
10191
}
10192
 
10193
function $IntervalProvider() {
10194
  this.$get = ['$rootScope', '$window', '$q', '$$q',
10195
       function($rootScope,   $window,   $q,   $$q) {
10196
    var intervals = {};
10197
 
10198
 
10199
     /**
10200
      * @ngdoc service
10201
      * @name $interval
10202
      *
10203
      * @description
10204
      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10205
      * milliseconds.
10206
      *
10207
      * The return value of registering an interval function is a promise. This promise will be
10208
      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10209
      * run indefinitely if `count` is not defined. The value of the notification will be the
10210
      * number of iterations that have run.
10211
      * To cancel an interval, call `$interval.cancel(promise)`.
10212
      *
10213
      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10214
      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10215
      * time.
10216
      *
10217
      * <div class="alert alert-warning">
10218
      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10219
      * with them.  In particular they are not automatically destroyed when a controller's scope or a
10220
      * directive's element are destroyed.
10221
      * You should take this into consideration and make sure to always cancel the interval at the
10222
      * appropriate moment.  See the example below for more details on how and when to do this.
10223
      * </div>
10224
      *
10225
      * @param {function()} fn A function that should be called repeatedly.
10226
      * @param {number} delay Number of milliseconds between each function call.
10227
      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10228
      *   indefinitely.
10229
      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10230
      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10231
      * @returns {promise} A promise which will be notified on each iteration.
10232
      *
10233
      * @example
10234
      * <example module="intervalExample">
10235
      * <file name="index.html">
10236
      *   <script>
10237
      *     angular.module('intervalExample', [])
10238
      *       .controller('ExampleController', ['$scope', '$interval',
10239
      *         function($scope, $interval) {
10240
      *           $scope.format = 'M/d/yy h:mm:ss a';
10241
      *           $scope.blood_1 = 100;
10242
      *           $scope.blood_2 = 120;
10243
      *
10244
      *           var stop;
10245
      *           $scope.fight = function() {
10246
      *             // Don't start a new fight if we are already fighting
10247
      *             if ( angular.isDefined(stop) ) return;
10248
      *
10249
      *             stop = $interval(function() {
10250
      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10251
      *                 $scope.blood_1 = $scope.blood_1 - 3;
10252
      *                 $scope.blood_2 = $scope.blood_2 - 4;
10253
      *               } else {
10254
      *                 $scope.stopFight();
10255
      *               }
10256
      *             }, 100);
10257
      *           };
10258
      *
10259
      *           $scope.stopFight = function() {
10260
      *             if (angular.isDefined(stop)) {
10261
      *               $interval.cancel(stop);
10262
      *               stop = undefined;
10263
      *             }
10264
      *           };
10265
      *
10266
      *           $scope.resetFight = function() {
10267
      *             $scope.blood_1 = 100;
10268
      *             $scope.blood_2 = 120;
10269
      *           };
10270
      *
10271
      *           $scope.$on('$destroy', function() {
10272
      *             // Make sure that the interval is destroyed too
10273
      *             $scope.stopFight();
10274
      *           });
10275
      *         }])
10276
      *       // Register the 'myCurrentTime' directive factory method.
10277
      *       // We inject $interval and dateFilter service since the factory method is DI.
10278
      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
10279
      *         function($interval, dateFilter) {
10280
      *           // return the directive link function. (compile function not needed)
10281
      *           return function(scope, element, attrs) {
10282
      *             var format,  // date format
10283
      *                 stopTime; // so that we can cancel the time updates
10284
      *
10285
      *             // used to update the UI
10286
      *             function updateTime() {
10287
      *               element.text(dateFilter(new Date(), format));
10288
      *             }
10289
      *
10290
      *             // watch the expression, and update the UI on change.
10291
      *             scope.$watch(attrs.myCurrentTime, function(value) {
10292
      *               format = value;
10293
      *               updateTime();
10294
      *             });
10295
      *
10296
      *             stopTime = $interval(updateTime, 1000);
10297
      *
10298
      *             // listen on DOM destroy (removal) event, and cancel the next UI update
10299
      *             // to prevent updating time after the DOM element was removed.
10300
      *             element.on('$destroy', function() {
10301
      *               $interval.cancel(stopTime);
10302
      *             });
10303
      *           }
10304
      *         }]);
10305
      *   </script>
10306
      *
10307
      *   <div>
10308
      *     <div ng-controller="ExampleController">
10309
      *       Date format: <input ng-model="format"> <hr/>
10310
      *       Current time is: <span my-current-time="format"></span>
10311
      *       <hr/>
10312
      *       Blood 1 : <font color='red'>{{blood_1}}</font>
10313
      *       Blood 2 : <font color='red'>{{blood_2}}</font>
10314
      *       <button type="button" data-ng-click="fight()">Fight</button>
10315
      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
10316
      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
10317
      *     </div>
10318
      *   </div>
10319
      *
10320
      * </file>
10321
      * </example>
10322
      */
10323
    function interval(fn, delay, count, invokeApply) {
10324
      var setInterval = $window.setInterval,
10325
          clearInterval = $window.clearInterval,
10326
          iteration = 0,
10327
          skipApply = (isDefined(invokeApply) && !invokeApply),
10328
          deferred = (skipApply ? $$q : $q).defer(),
10329
          promise = deferred.promise;
10330
 
10331
      count = isDefined(count) ? count : 0;
10332
 
10333
      promise.then(null, null, fn);
10334
 
10335
      promise.$$intervalId = setInterval(function tick() {
10336
        deferred.notify(iteration++);
10337
 
10338
        if (count > 0 && iteration >= count) {
10339
          deferred.resolve(iteration);
10340
          clearInterval(promise.$$intervalId);
10341
          delete intervals[promise.$$intervalId];
10342
        }
10343
 
10344
        if (!skipApply) $rootScope.$apply();
10345
 
10346
      }, delay);
10347
 
10348
      intervals[promise.$$intervalId] = deferred;
10349
 
10350
      return promise;
10351
    }
10352
 
10353
 
10354
     /**
10355
      * @ngdoc method
10356
      * @name $interval#cancel
10357
      *
10358
      * @description
10359
      * Cancels a task associated with the `promise`.
10360
      *
10361
      * @param {promise} promise returned by the `$interval` function.
10362
      * @returns {boolean} Returns `true` if the task was successfully canceled.
10363
      */
10364
    interval.cancel = function(promise) {
10365
      if (promise && promise.$$intervalId in intervals) {
10366
        intervals[promise.$$intervalId].reject('canceled');
10367
        $window.clearInterval(promise.$$intervalId);
10368
        delete intervals[promise.$$intervalId];
10369
        return true;
10370
      }
10371
      return false;
10372
    };
10373
 
10374
    return interval;
10375
  }];
10376
}
10377
 
10378
/**
10379
 * @ngdoc service
10380
 * @name $locale
10381
 *
10382
 * @description
10383
 * $locale service provides localization rules for various Angular components. As of right now the
10384
 * only public api is:
10385
 *
10386
 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
10387
 */
10388
function $LocaleProvider() {
10389
  this.$get = function() {
10390
    return {
10391
      id: 'en-us',
10392
 
10393
      NUMBER_FORMATS: {
10394
        DECIMAL_SEP: '.',
10395
        GROUP_SEP: ',',
10396
        PATTERNS: [
10397
          { // Decimal Pattern
10398
            minInt: 1,
10399
            minFrac: 0,
10400
            maxFrac: 3,
10401
            posPre: '',
10402
            posSuf: '',
10403
            negPre: '-',
10404
            negSuf: '',
10405
            gSize: 3,
10406
            lgSize: 3
10407
          },{ //Currency Pattern
10408
            minInt: 1,
10409
            minFrac: 2,
10410
            maxFrac: 2,
10411
            posPre: '\u00A4',
10412
            posSuf: '',
10413
            negPre: '(\u00A4',
10414
            negSuf: ')',
10415
            gSize: 3,
10416
            lgSize: 3
10417
          }
10418
        ],
10419
        CURRENCY_SYM: '$'
10420
      },
10421
 
10422
      DATETIME_FORMATS: {
10423
        MONTH:
10424
            'January,February,March,April,May,June,July,August,September,October,November,December'
10425
            .split(','),
10426
        SHORTMONTH:  'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
10427
        DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
10428
        SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
10429
        AMPMS: ['AM','PM'],
10430
        medium: 'MMM d, y h:mm:ss a',
10431
        'short': 'M/d/yy h:mm a',
10432
        fullDate: 'EEEE, MMMM d, y',
10433
        longDate: 'MMMM d, y',
10434
        mediumDate: 'MMM d, y',
10435
        shortDate: 'M/d/yy',
10436
        mediumTime: 'h:mm:ss a',
10437
        shortTime: 'h:mm a'
10438
      },
10439
 
10440
      pluralCat: function(num) {
10441
        if (num === 1) {
10442
          return 'one';
10443
        }
10444
        return 'other';
10445
      }
10446
    };
10447
  };
10448
}
10449
 
10450
var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
10451
    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
10452
var $locationMinErr = minErr('$location');
10453
 
10454
 
10455
/**
10456
 * Encode path using encodeUriSegment, ignoring forward slashes
10457
 *
10458
 * @param {string} path Path to encode
10459
 * @returns {string}
10460
 */
10461
function encodePath(path) {
10462
  var segments = path.split('/'),
10463
      i = segments.length;
10464
 
10465
  while (i--) {
10466
    segments[i] = encodeUriSegment(segments[i]);
10467
  }
10468
 
10469
  return segments.join('/');
10470
}
10471
 
10472
function parseAbsoluteUrl(absoluteUrl, locationObj) {
10473
  var parsedUrl = urlResolve(absoluteUrl);
10474
 
10475
  locationObj.$$protocol = parsedUrl.protocol;
10476
  locationObj.$$host = parsedUrl.hostname;
10477
  locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
10478
}
10479
 
10480
 
10481
function parseAppUrl(relativeUrl, locationObj) {
10482
  var prefixed = (relativeUrl.charAt(0) !== '/');
10483
  if (prefixed) {
10484
    relativeUrl = '/' + relativeUrl;
10485
  }
10486
  var match = urlResolve(relativeUrl);
10487
  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10488
      match.pathname.substring(1) : match.pathname);
10489
  locationObj.$$search = parseKeyValue(match.search);
10490
  locationObj.$$hash = decodeURIComponent(match.hash);
10491
 
10492
  // make sure path starts with '/';
10493
  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
10494
    locationObj.$$path = '/' + locationObj.$$path;
10495
  }
10496
}
10497
 
10498
 
10499
/**
10500
 *
10501
 * @param {string} begin
10502
 * @param {string} whole
10503
 * @returns {string} returns text from whole after begin or undefined if it does not begin with
10504
 *                   expected string.
10505
 */
10506
function beginsWith(begin, whole) {
10507
  if (whole.indexOf(begin) === 0) {
10508
    return whole.substr(begin.length);
10509
  }
10510
}
10511
 
10512
 
10513
function stripHash(url) {
10514
  var index = url.indexOf('#');
10515
  return index == -1 ? url : url.substr(0, index);
10516
}
10517
 
10518
function trimEmptyHash(url) {
10519
  return url.replace(/(#.+)|#$/, '$1');
10520
}
10521
 
10522
 
10523
function stripFile(url) {
10524
  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
10525
}
10526
 
10527
/* return the server only (scheme://host:port) */
10528
function serverBase(url) {
10529
  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
10530
}
10531
 
10532
 
10533
/**
10534
 * LocationHtml5Url represents an url
10535
 * This object is exposed as $location service when HTML5 mode is enabled and supported
10536
 *
10537
 * @constructor
10538
 * @param {string} appBase application base URL
10539
 * @param {string} basePrefix url path prefix
10540
 */
10541
function LocationHtml5Url(appBase, basePrefix) {
10542
  this.$$html5 = true;
10543
  basePrefix = basePrefix || '';
10544
  var appBaseNoFile = stripFile(appBase);
10545
  parseAbsoluteUrl(appBase, this);
10546
 
10547
 
10548
  /**
10549
   * Parse given html5 (regular) url string into properties
10550
   * @param {string} url HTML5 url
10551
   * @private
10552
   */
10553
  this.$$parse = function(url) {
10554
    var pathUrl = beginsWith(appBaseNoFile, url);
10555
    if (!isString(pathUrl)) {
10556
      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
10557
          appBaseNoFile);
10558
    }
10559
 
10560
    parseAppUrl(pathUrl, this);
10561
 
10562
    if (!this.$$path) {
10563
      this.$$path = '/';
10564
    }
10565
 
10566
    this.$$compose();
10567
  };
10568
 
10569
  /**
10570
   * Compose url and update `absUrl` property
10571
   * @private
10572
   */
10573
  this.$$compose = function() {
10574
    var search = toKeyValue(this.$$search),
10575
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10576
 
10577
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10578
    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
10579
  };
10580
 
10581
  this.$$parseLinkUrl = function(url, relHref) {
10582
    if (relHref && relHref[0] === '#') {
10583
      // special case for links to hash fragments:
10584
      // keep the old url and only replace the hash fragment
10585
      this.hash(relHref.slice(1));
10586
      return true;
10587
    }
10588
    var appUrl, prevAppUrl;
10589
    var rewrittenUrl;
10590
 
10591
    if ((appUrl = beginsWith(appBase, url)) !== undefined) {
10592
      prevAppUrl = appUrl;
10593
      if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
10594
        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
10595
      } else {
10596
        rewrittenUrl = appBase + prevAppUrl;
10597
      }
10598
    } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
10599
      rewrittenUrl = appBaseNoFile + appUrl;
10600
    } else if (appBaseNoFile == url + '/') {
10601
      rewrittenUrl = appBaseNoFile;
10602
    }
10603
    if (rewrittenUrl) {
10604
      this.$$parse(rewrittenUrl);
10605
    }
10606
    return !!rewrittenUrl;
10607
  };
10608
}
10609
 
10610
 
10611
/**
10612
 * LocationHashbangUrl represents url
10613
 * This object is exposed as $location service when developer doesn't opt into html5 mode.
10614
 * It also serves as the base class for html5 mode fallback on legacy browsers.
10615
 *
10616
 * @constructor
10617
 * @param {string} appBase application base URL
10618
 * @param {string} hashPrefix hashbang prefix
10619
 */
10620
function LocationHashbangUrl(appBase, hashPrefix) {
10621
  var appBaseNoFile = stripFile(appBase);
10622
 
10623
  parseAbsoluteUrl(appBase, this);
10624
 
10625
 
10626
  /**
10627
   * Parse given hashbang url into properties
10628
   * @param {string} url Hashbang url
10629
   * @private
10630
   */
10631
  this.$$parse = function(url) {
10632
    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
10633
    var withoutHashUrl;
10634
 
10635
    if (withoutBaseUrl.charAt(0) === '#') {
10636
 
10637
      // The rest of the url starts with a hash so we have
10638
      // got either a hashbang path or a plain hash fragment
10639
      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
10640
      if (isUndefined(withoutHashUrl)) {
10641
        // There was no hashbang prefix so we just have a hash fragment
10642
        withoutHashUrl = withoutBaseUrl;
10643
      }
10644
 
10645
    } else {
10646
      // There was no hashbang path nor hash fragment:
10647
      // If we are in HTML5 mode we use what is left as the path;
10648
      // Otherwise we ignore what is left
10649
      withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
10650
    }
10651
 
10652
    parseAppUrl(withoutHashUrl, this);
10653
 
10654
    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10655
 
10656
    this.$$compose();
10657
 
10658
    /*
10659
     * In Windows, on an anchor node on documents loaded from
10660
     * the filesystem, the browser will return a pathname
10661
     * prefixed with the drive name ('/C:/path') when a
10662
     * pathname without a drive is set:
10663
     *  * a.setAttribute('href', '/foo')
10664
     *   * a.pathname === '/C:/foo' //true
10665
     *
10666
     * Inside of Angular, we're always using pathnames that
10667
     * do not include drive names for routing.
10668
     */
10669
    function removeWindowsDriveName(path, url, base) {
10670
      /*
10671
      Matches paths for file protocol on windows,
10672
      such as /C:/foo/bar, and captures only /foo/bar.
10673
      */
10674
      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
10675
 
10676
      var firstPathSegmentMatch;
10677
 
10678
      //Get the relative path from the input URL.
10679
      if (url.indexOf(base) === 0) {
10680
        url = url.replace(base, '');
10681
      }
10682
 
10683
      // The input URL intentionally contains a first path segment that ends with a colon.
10684
      if (windowsFilePathExp.exec(url)) {
10685
        return path;
10686
      }
10687
 
10688
      firstPathSegmentMatch = windowsFilePathExp.exec(path);
10689
      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
10690
    }
10691
  };
10692
 
10693
  /**
10694
   * Compose hashbang url and update `absUrl` property
10695
   * @private
10696
   */
10697
  this.$$compose = function() {
10698
    var search = toKeyValue(this.$$search),
10699
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10700
 
10701
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10702
    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
10703
  };
10704
 
10705
  this.$$parseLinkUrl = function(url, relHref) {
10706
    if (stripHash(appBase) == stripHash(url)) {
10707
      this.$$parse(url);
10708
      return true;
10709
    }
10710
    return false;
10711
  };
10712
}
10713
 
10714
 
10715
/**
10716
 * LocationHashbangUrl represents url
10717
 * This object is exposed as $location service when html5 history api is enabled but the browser
10718
 * does not support it.
10719
 *
10720
 * @constructor
10721
 * @param {string} appBase application base URL
10722
 * @param {string} hashPrefix hashbang prefix
10723
 */
10724
function LocationHashbangInHtml5Url(appBase, hashPrefix) {
10725
  this.$$html5 = true;
10726
  LocationHashbangUrl.apply(this, arguments);
10727
 
10728
  var appBaseNoFile = stripFile(appBase);
10729
 
10730
  this.$$parseLinkUrl = function(url, relHref) {
10731
    if (relHref && relHref[0] === '#') {
10732
      // special case for links to hash fragments:
10733
      // keep the old url and only replace the hash fragment
10734
      this.hash(relHref.slice(1));
10735
      return true;
10736
    }
10737
 
10738
    var rewrittenUrl;
10739
    var appUrl;
10740
 
10741
    if (appBase == stripHash(url)) {
10742
      rewrittenUrl = url;
10743
    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
10744
      rewrittenUrl = appBase + hashPrefix + appUrl;
10745
    } else if (appBaseNoFile === url + '/') {
10746
      rewrittenUrl = appBaseNoFile;
10747
    }
10748
    if (rewrittenUrl) {
10749
      this.$$parse(rewrittenUrl);
10750
    }
10751
    return !!rewrittenUrl;
10752
  };
10753
 
10754
  this.$$compose = function() {
10755
    var search = toKeyValue(this.$$search),
10756
        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10757
 
10758
    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10759
    // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
10760
    this.$$absUrl = appBase + hashPrefix + this.$$url;
10761
  };
10762
 
10763
}
10764
 
10765
 
10766
var locationPrototype = {
10767
 
10768
  /**
10769
   * Are we in html5 mode?
10770
   * @private
10771
   */
10772
  $$html5: false,
10773
 
10774
  /**
10775
   * Has any change been replacing?
10776
   * @private
10777
   */
10778
  $$replace: false,
10779
 
10780
  /**
10781
   * @ngdoc method
10782
   * @name $location#absUrl
10783
   *
10784
   * @description
10785
   * This method is getter only.
10786
   *
10787
   * Return full url representation with all segments encoded according to rules specified in
10788
   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10789
   *
10790
   *
10791
   * ```js
10792
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10793
   * var absUrl = $location.absUrl();
10794
   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10795
   * ```
10796
   *
10797
   * @return {string} full url
10798
   */
10799
  absUrl: locationGetter('$$absUrl'),
10800
 
10801
  /**
10802
   * @ngdoc method
10803
   * @name $location#url
10804
   *
10805
   * @description
10806
   * This method is getter / setter.
10807
   *
10808
   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
10809
   *
10810
   * Change path, search and hash, when called with parameter and return `$location`.
10811
   *
10812
   *
10813
   * ```js
10814
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10815
   * var url = $location.url();
10816
   * // => "/some/path?foo=bar&baz=xoxo"
10817
   * ```
10818
   *
10819
   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10820
   * @return {string} url
10821
   */
10822
  url: function(url) {
10823
    if (isUndefined(url))
10824
      return this.$$url;
10825
 
10826
    var match = PATH_MATCH.exec(url);
10827
    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10828
    if (match[2] || match[1] || url === '') this.search(match[3] || '');
10829
    this.hash(match[5] || '');
10830
 
10831
    return this;
10832
  },
10833
 
10834
  /**
10835
   * @ngdoc method
10836
   * @name $location#protocol
10837
   *
10838
   * @description
10839
   * This method is getter only.
10840
   *
10841
   * Return protocol of current url.
10842
   *
10843
   *
10844
   * ```js
10845
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10846
   * var protocol = $location.protocol();
10847
   * // => "http"
10848
   * ```
10849
   *
10850
   * @return {string} protocol of current url
10851
   */
10852
  protocol: locationGetter('$$protocol'),
10853
 
10854
  /**
10855
   * @ngdoc method
10856
   * @name $location#host
10857
   *
10858
   * @description
10859
   * This method is getter only.
10860
   *
10861
   * Return host of current url.
10862
   *
10863
   *
10864
   * ```js
10865
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10866
   * var host = $location.host();
10867
   * // => "example.com"
10868
   * ```
10869
   *
10870
   * @return {string} host of current url.
10871
   */
10872
  host: locationGetter('$$host'),
10873
 
10874
  /**
10875
   * @ngdoc method
10876
   * @name $location#port
10877
   *
10878
   * @description
10879
   * This method is getter only.
10880
   *
10881
   * Return port of current url.
10882
   *
10883
   *
10884
   * ```js
10885
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10886
   * var port = $location.port();
10887
   * // => 80
10888
   * ```
10889
   *
10890
   * @return {Number} port
10891
   */
10892
  port: locationGetter('$$port'),
10893
 
10894
  /**
10895
   * @ngdoc method
10896
   * @name $location#path
10897
   *
10898
   * @description
10899
   * This method is getter / setter.
10900
   *
10901
   * Return path of current url when called without any parameter.
10902
   *
10903
   * Change path when called with parameter and return `$location`.
10904
   *
10905
   * Note: Path should always begin with forward slash (/), this method will add the forward slash
10906
   * if it is missing.
10907
   *
10908
   *
10909
   * ```js
10910
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10911
   * var path = $location.path();
10912
   * // => "/some/path"
10913
   * ```
10914
   *
10915
   * @param {(string|number)=} path New path
10916
   * @return {string} path
10917
   */
10918
  path: locationGetterSetter('$$path', function(path) {
10919
    path = path !== null ? path.toString() : '';
10920
    return path.charAt(0) == '/' ? path : '/' + path;
10921
  }),
10922
 
10923
  /**
10924
   * @ngdoc method
10925
   * @name $location#search
10926
   *
10927
   * @description
10928
   * This method is getter / setter.
10929
   *
10930
   * Return search part (as object) of current url when called without any parameter.
10931
   *
10932
   * Change search part when called with parameter and return `$location`.
10933
   *
10934
   *
10935
   * ```js
10936
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10937
   * var searchObject = $location.search();
10938
   * // => {foo: 'bar', baz: 'xoxo'}
10939
   *
10940
   * // set foo to 'yipee'
10941
   * $location.search('foo', 'yipee');
10942
   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
10943
   * ```
10944
   *
10945
   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
10946
   * hash object.
10947
   *
10948
   * When called with a single argument the method acts as a setter, setting the `search` component
10949
   * of `$location` to the specified value.
10950
   *
10951
   * If the argument is a hash object containing an array of values, these values will be encoded
10952
   * as duplicate search parameters in the url.
10953
   *
10954
   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
10955
   * will override only a single search property.
10956
   *
10957
   * If `paramValue` is an array, it will override the property of the `search` component of
10958
   * `$location` specified via the first argument.
10959
   *
10960
   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
10961
   *
10962
   * If `paramValue` is `true`, the property specified via the first argument will be added with no
10963
   * value nor trailing equal sign.
10964
   *
10965
   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
10966
   * one or more arguments returns `$location` object itself.
10967
   */
10968
  search: function(search, paramValue) {
10969
    switch (arguments.length) {
10970
      case 0:
10971
        return this.$$search;
10972
      case 1:
10973
        if (isString(search) || isNumber(search)) {
10974
          search = search.toString();
10975
          this.$$search = parseKeyValue(search);
10976
        } else if (isObject(search)) {
10977
          search = copy(search, {});
10978
          // remove object undefined or null properties
10979
          forEach(search, function(value, key) {
10980
            if (value == null) delete search[key];
10981
          });
10982
 
10983
          this.$$search = search;
10984
        } else {
10985
          throw $locationMinErr('isrcharg',
10986
              'The first argument of the `$location#search()` call must be a string or an object.');
10987
        }
10988
        break;
10989
      default:
10990
        if (isUndefined(paramValue) || paramValue === null) {
10991
          delete this.$$search[search];
10992
        } else {
10993
          this.$$search[search] = paramValue;
10994
        }
10995
    }
10996
 
10997
    this.$$compose();
10998
    return this;
10999
  },
11000
 
11001
  /**
11002
   * @ngdoc method
11003
   * @name $location#hash
11004
   *
11005
   * @description
11006
   * This method is getter / setter.
11007
   *
11008
   * Return hash fragment when called without any parameter.
11009
   *
11010
   * Change hash fragment when called with parameter and return `$location`.
11011
   *
11012
   *
11013
   * ```js
11014
   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11015
   * var hash = $location.hash();
11016
   * // => "hashValue"
11017
   * ```
11018
   *
11019
   * @param {(string|number)=} hash New hash fragment
11020
   * @return {string} hash
11021
   */
11022
  hash: locationGetterSetter('$$hash', function(hash) {
11023
    return hash !== null ? hash.toString() : '';
11024
  }),
11025
 
11026
  /**
11027
   * @ngdoc method
11028
   * @name $location#replace
11029
   *
11030
   * @description
11031
   * If called, all changes to $location during current `$digest` will be replacing current history
11032
   * record, instead of adding new one.
11033
   */
11034
  replace: function() {
11035
    this.$$replace = true;
11036
    return this;
11037
  }
11038
};
11039
 
11040
forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11041
  Location.prototype = Object.create(locationPrototype);
11042
 
11043
  /**
11044
   * @ngdoc method
11045
   * @name $location#state
11046
   *
11047
   * @description
11048
   * This method is getter / setter.
11049
   *
11050
   * Return the history state object when called without any parameter.
11051
   *
11052
   * Change the history state object when called with one parameter and return `$location`.
11053
   * The state object is later passed to `pushState` or `replaceState`.
11054
   *
11055
   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11056
   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11057
   * older browsers (like IE9 or Android < 4.0), don't use this method.
11058
   *
11059
   * @param {object=} state State object for pushState or replaceState
11060
   * @return {object} state
11061
   */
11062
  Location.prototype.state = function(state) {
11063
    if (!arguments.length)
11064
      return this.$$state;
11065
 
11066
    if (Location !== LocationHtml5Url || !this.$$html5) {
11067
      throw $locationMinErr('nostate', 'History API state support is available only ' +
11068
        'in HTML5 mode and only in browsers supporting HTML5 History API');
11069
    }
11070
    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11071
    // but we're changing the $$state reference to $browser.state() during the $digest
11072
    // so the modification window is narrow.
11073
    this.$$state = isUndefined(state) ? null : state;
11074
 
11075
    return this;
11076
  };
11077
});
11078
 
11079
 
11080
function locationGetter(property) {
11081
  return function() {
11082
    return this[property];
11083
  };
11084
}
11085
 
11086
 
11087
function locationGetterSetter(property, preprocess) {
11088
  return function(value) {
11089
    if (isUndefined(value))
11090
      return this[property];
11091
 
11092
    this[property] = preprocess(value);
11093
    this.$$compose();
11094
 
11095
    return this;
11096
  };
11097
}
11098
 
11099
 
11100
/**
11101
 * @ngdoc service
11102
 * @name $location
11103
 *
11104
 * @requires $rootElement
11105
 *
11106
 * @description
11107
 * The $location service parses the URL in the browser address bar (based on the
11108
 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11109
 * available to your application. Changes to the URL in the address bar are reflected into
11110
 * $location service and changes to $location are reflected into the browser address bar.
11111
 *
11112
 * **The $location service:**
11113
 *
11114
 * - Exposes the current URL in the browser address bar, so you can
11115
 *   - Watch and observe the URL.
11116
 *   - Change the URL.
11117
 * - Synchronizes the URL with the browser when the user
11118
 *   - Changes the address bar.
11119
 *   - Clicks the back or forward button (or clicks a History link).
11120
 *   - Clicks on a link.
11121
 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11122
 *
11123
 * For more information see {@link guide/$location Developer Guide: Using $location}
11124
 */
11125
 
11126
/**
11127
 * @ngdoc provider
11128
 * @name $locationProvider
11129
 * @description
11130
 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11131
 */
11132
function $LocationProvider() {
11133
  var hashPrefix = '',
11134
      html5Mode = {
11135
        enabled: false,
11136
        requireBase: true,
11137
        rewriteLinks: true
11138
      };
11139
 
11140
  /**
11141
   * @ngdoc method
11142
   * @name $locationProvider#hashPrefix
11143
   * @description
11144
   * @param {string=} prefix Prefix for hash part (containing path and search)
11145
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
11146
   */
11147
  this.hashPrefix = function(prefix) {
11148
    if (isDefined(prefix)) {
11149
      hashPrefix = prefix;
11150
      return this;
11151
    } else {
11152
      return hashPrefix;
11153
    }
11154
  };
11155
 
11156
  /**
11157
   * @ngdoc method
11158
   * @name $locationProvider#html5Mode
11159
   * @description
11160
   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11161
   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11162
   *   properties:
11163
   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11164
   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11165
   *     support `pushState`.
11166
   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11167
   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11168
   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
11169
   *     See the {@link guide/$location $location guide for more information}
11170
   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11171
   *     enables/disables url rewriting for relative links.
11172
   *
11173
   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11174
   */
11175
  this.html5Mode = function(mode) {
11176
    if (isBoolean(mode)) {
11177
      html5Mode.enabled = mode;
11178
      return this;
11179
    } else if (isObject(mode)) {
11180
 
11181
      if (isBoolean(mode.enabled)) {
11182
        html5Mode.enabled = mode.enabled;
11183
      }
11184
 
11185
      if (isBoolean(mode.requireBase)) {
11186
        html5Mode.requireBase = mode.requireBase;
11187
      }
11188
 
11189
      if (isBoolean(mode.rewriteLinks)) {
11190
        html5Mode.rewriteLinks = mode.rewriteLinks;
11191
      }
11192
 
11193
      return this;
11194
    } else {
11195
      return html5Mode;
11196
    }
11197
  };
11198
 
11199
  /**
11200
   * @ngdoc event
11201
   * @name $location#$locationChangeStart
11202
   * @eventType broadcast on root scope
11203
   * @description
11204
   * Broadcasted before a URL will change.
11205
   *
11206
   * This change can be prevented by calling
11207
   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11208
   * details about event object. Upon successful change
11209
   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11210
   *
11211
   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11212
   * the browser supports the HTML5 History API.
11213
   *
11214
   * @param {Object} angularEvent Synthetic event object.
11215
   * @param {string} newUrl New URL
11216
   * @param {string=} oldUrl URL that was before it was changed.
11217
   * @param {string=} newState New history state object
11218
   * @param {string=} oldState History state object that was before it was changed.
11219
   */
11220
 
11221
  /**
11222
   * @ngdoc event
11223
   * @name $location#$locationChangeSuccess
11224
   * @eventType broadcast on root scope
11225
   * @description
11226
   * Broadcasted after a URL was changed.
11227
   *
11228
   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11229
   * the browser supports the HTML5 History API.
11230
   *
11231
   * @param {Object} angularEvent Synthetic event object.
11232
   * @param {string} newUrl New URL
11233
   * @param {string=} oldUrl URL that was before it was changed.
11234
   * @param {string=} newState New history state object
11235
   * @param {string=} oldState History state object that was before it was changed.
11236
   */
11237
 
11238
  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
11239
      function($rootScope, $browser, $sniffer, $rootElement) {
11240
    var $location,
11241
        LocationMode,
11242
        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11243
        initialUrl = $browser.url(),
11244
        appBase;
11245
 
11246
    if (html5Mode.enabled) {
11247
      if (!baseHref && html5Mode.requireBase) {
11248
        throw $locationMinErr('nobase',
11249
          "$location in HTML5 mode requires a <base> tag to be present!");
11250
      }
11251
      appBase = serverBase(initialUrl) + (baseHref || '/');
11252
      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
11253
    } else {
11254
      appBase = stripHash(initialUrl);
11255
      LocationMode = LocationHashbangUrl;
11256
    }
11257
    $location = new LocationMode(appBase, '#' + hashPrefix);
11258
    $location.$$parseLinkUrl(initialUrl, initialUrl);
11259
 
11260
    $location.$$state = $browser.state();
11261
 
11262
    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
11263
 
11264
    function setBrowserUrlWithFallback(url, replace, state) {
11265
      var oldUrl = $location.url();
11266
      var oldState = $location.$$state;
11267
      try {
11268
        $browser.url(url, replace, state);
11269
 
11270
        // Make sure $location.state() returns referentially identical (not just deeply equal)
11271
        // state object; this makes possible quick checking if the state changed in the digest
11272
        // loop. Checking deep equality would be too expensive.
11273
        $location.$$state = $browser.state();
11274
      } catch (e) {
11275
        // Restore old values if pushState fails
11276
        $location.url(oldUrl);
11277
        $location.$$state = oldState;
11278
 
11279
        throw e;
11280
      }
11281
    }
11282
 
11283
    $rootElement.on('click', function(event) {
11284
      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
11285
      // currently we open nice url link and redirect then
11286
 
11287
      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.which == 2) return;
11288
 
11289
      var elm = jqLite(event.target);
11290
 
11291
      // traverse the DOM up to find first A tag
11292
      while (nodeName_(elm[0]) !== 'a') {
11293
        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
11294
        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
11295
      }
11296
 
11297
      var absHref = elm.prop('href');
11298
      // get the actual href attribute - see
11299
      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
11300
      var relHref = elm.attr('href') || elm.attr('xlink:href');
11301
 
11302
      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
11303
        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
11304
        // an animation.
11305
        absHref = urlResolve(absHref.animVal).href;
11306
      }
11307
 
11308
      // Ignore when url is started with javascript: or mailto:
11309
      if (IGNORE_URI_REGEXP.test(absHref)) return;
11310
 
11311
      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
11312
        if ($location.$$parseLinkUrl(absHref, relHref)) {
11313
          // We do a preventDefault for all urls that are part of the angular application,
11314
          // in html5mode and also without, so that we are able to abort navigation without
11315
          // getting double entries in the location history.
11316
          event.preventDefault();
11317
          // update location manually
11318
          if ($location.absUrl() != $browser.url()) {
11319
            $rootScope.$apply();
11320
            // hack to work around FF6 bug 684208 when scenario runner clicks on links
11321
            window.angular['ff-684208-preventDefault'] = true;
11322
          }
11323
        }
11324
      }
11325
    });
11326
 
11327
 
11328
    // rewrite hashbang url <> html5 url
11329
    if ($location.absUrl() != initialUrl) {
11330
      $browser.url($location.absUrl(), true);
11331
    }
11332
 
11333
    var initializing = true;
11334
 
11335
    // update $location when $browser url changes
11336
    $browser.onUrlChange(function(newUrl, newState) {
11337
      $rootScope.$evalAsync(function() {
11338
        var oldUrl = $location.absUrl();
11339
        var oldState = $location.$$state;
11340
        var defaultPrevented;
11341
 
11342
        $location.$$parse(newUrl);
11343
        $location.$$state = newState;
11344
 
11345
        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11346
            newState, oldState).defaultPrevented;
11347
 
11348
        // if the location was changed by a `$locationChangeStart` handler then stop
11349
        // processing this location change
11350
        if ($location.absUrl() !== newUrl) return;
11351
 
11352
        if (defaultPrevented) {
11353
          $location.$$parse(oldUrl);
11354
          $location.$$state = oldState;
11355
          setBrowserUrlWithFallback(oldUrl, false, oldState);
11356
        } else {
11357
          initializing = false;
11358
          afterLocationChange(oldUrl, oldState);
11359
        }
11360
      });
11361
      if (!$rootScope.$$phase) $rootScope.$digest();
11362
    });
11363
 
11364
    // update browser
11365
    $rootScope.$watch(function $locationWatch() {
11366
      var oldUrl = trimEmptyHash($browser.url());
11367
      var newUrl = trimEmptyHash($location.absUrl());
11368
      var oldState = $browser.state();
11369
      var currentReplace = $location.$$replace;
11370
      var urlOrStateChanged = oldUrl !== newUrl ||
11371
        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
11372
 
11373
      if (initializing || urlOrStateChanged) {
11374
        initializing = false;
11375
 
11376
        $rootScope.$evalAsync(function() {
11377
          var newUrl = $location.absUrl();
11378
          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11379
              $location.$$state, oldState).defaultPrevented;
11380
 
11381
          // if the location was changed by a `$locationChangeStart` handler then stop
11382
          // processing this location change
11383
          if ($location.absUrl() !== newUrl) return;
11384
 
11385
          if (defaultPrevented) {
11386
            $location.$$parse(oldUrl);
11387
            $location.$$state = oldState;
11388
          } else {
11389
            if (urlOrStateChanged) {
11390
              setBrowserUrlWithFallback(newUrl, currentReplace,
11391
                                        oldState === $location.$$state ? null : $location.$$state);
11392
            }
11393
            afterLocationChange(oldUrl, oldState);
11394
          }
11395
        });
11396
      }
11397
 
11398
      $location.$$replace = false;
11399
 
11400
      // we don't need to return anything because $evalAsync will make the digest loop dirty when
11401
      // there is a change
11402
    });
11403
 
11404
    return $location;
11405
 
11406
    function afterLocationChange(oldUrl, oldState) {
11407
      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
11408
        $location.$$state, oldState);
11409
    }
11410
}];
11411
}
11412
 
11413
/**
11414
 * @ngdoc service
11415
 * @name $log
11416
 * @requires $window
11417
 *
11418
 * @description
11419
 * Simple service for logging. Default implementation safely writes the message
11420
 * into the browser's console (if present).
11421
 *
11422
 * The main purpose of this service is to simplify debugging and troubleshooting.
11423
 *
11424
 * The default is to log `debug` messages. You can use
11425
 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
11426
 *
11427
 * @example
11428
   <example module="logExample">
11429
     <file name="script.js">
11430
       angular.module('logExample', [])
11431
         .controller('LogController', ['$scope', '$log', function($scope, $log) {
11432
           $scope.$log = $log;
11433
           $scope.message = 'Hello World!';
11434
         }]);
11435
     </file>
11436
     <file name="index.html">
11437
       <div ng-controller="LogController">
11438
         <p>Reload this page with open console, enter text and hit the log button...</p>
11439
         Message:
11440
         <input type="text" ng-model="message"/>
11441
         <button ng-click="$log.log(message)">log</button>
11442
         <button ng-click="$log.warn(message)">warn</button>
11443
         <button ng-click="$log.info(message)">info</button>
11444
         <button ng-click="$log.error(message)">error</button>
11445
       </div>
11446
     </file>
11447
   </example>
11448
 */
11449
 
11450
/**
11451
 * @ngdoc provider
11452
 * @name $logProvider
11453
 * @description
11454
 * Use the `$logProvider` to configure how the application logs messages
11455
 */
11456
function $LogProvider() {
11457
  var debug = true,
11458
      self = this;
11459
 
11460
  /**
11461
   * @ngdoc method
11462
   * @name $logProvider#debugEnabled
11463
   * @description
11464
   * @param {boolean=} flag enable or disable debug level messages
11465
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
11466
   */
11467
  this.debugEnabled = function(flag) {
11468
    if (isDefined(flag)) {
11469
      debug = flag;
11470
    return this;
11471
    } else {
11472
      return debug;
11473
    }
11474
  };
11475
 
11476
  this.$get = ['$window', function($window) {
11477
    return {
11478
      /**
11479
       * @ngdoc method
11480
       * @name $log#log
11481
       *
11482
       * @description
11483
       * Write a log message
11484
       */
11485
      log: consoleLog('log'),
11486
 
11487
      /**
11488
       * @ngdoc method
11489
       * @name $log#info
11490
       *
11491
       * @description
11492
       * Write an information message
11493
       */
11494
      info: consoleLog('info'),
11495
 
11496
      /**
11497
       * @ngdoc method
11498
       * @name $log#warn
11499
       *
11500
       * @description
11501
       * Write a warning message
11502
       */
11503
      warn: consoleLog('warn'),
11504
 
11505
      /**
11506
       * @ngdoc method
11507
       * @name $log#error
11508
       *
11509
       * @description
11510
       * Write an error message
11511
       */
11512
      error: consoleLog('error'),
11513
 
11514
      /**
11515
       * @ngdoc method
11516
       * @name $log#debug
11517
       *
11518
       * @description
11519
       * Write a debug message
11520
       */
11521
      debug: (function() {
11522
        var fn = consoleLog('debug');
11523
 
11524
        return function() {
11525
          if (debug) {
11526
            fn.apply(self, arguments);
11527
          }
11528
        };
11529
      }())
11530
    };
11531
 
11532
    function formatError(arg) {
11533
      if (arg instanceof Error) {
11534
        if (arg.stack) {
11535
          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
11536
              ? 'Error: ' + arg.message + '\n' + arg.stack
11537
              : arg.stack;
11538
        } else if (arg.sourceURL) {
11539
          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
11540
        }
11541
      }
11542
      return arg;
11543
    }
11544
 
11545
    function consoleLog(type) {
11546
      var console = $window.console || {},
11547
          logFn = console[type] || console.log || noop,
11548
          hasApply = false;
11549
 
11550
      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
11551
      // The reason behind this is that console.log has type "object" in IE8...
11552
      try {
11553
        hasApply = !!logFn.apply;
11554
      } catch (e) {}
11555
 
11556
      if (hasApply) {
11557
        return function() {
11558
          var args = [];
11559
          forEach(arguments, function(arg) {
11560
            args.push(formatError(arg));
11561
          });
11562
          return logFn.apply(console, args);
11563
        };
11564
      }
11565
 
11566
      // we are IE which either doesn't have window.console => this is noop and we do nothing,
11567
      // or we are IE where console.log doesn't have apply so we log at least first 2 args
11568
      return function(arg1, arg2) {
11569
        logFn(arg1, arg2 == null ? '' : arg2);
11570
      };
11571
    }
11572
  }];
11573
}
11574
 
11575
var $parseMinErr = minErr('$parse');
11576
 
11577
// Sandboxing Angular Expressions
11578
// ------------------------------
11579
// Angular expressions are generally considered safe because these expressions only have direct
11580
// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11581
// obtaining a reference to native JS functions such as the Function constructor.
11582
//
11583
// As an example, consider the following Angular expression:
11584
//
11585
//   {}.toString.constructor('alert("evil JS code")')
11586
//
11587
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11588
// against the expression language, but not to prevent exploits that were enabled by exposing
11589
// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11590
// practice and therefore we are not even trying to protect against interaction with an object
11591
// explicitly exposed in this way.
11592
//
11593
// In general, it is not possible to access a Window object from an angular expression unless a
11594
// window or some DOM object that has a reference to window is published onto a Scope.
11595
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11596
// native objects.
11597
//
11598
// See https://docs.angularjs.org/guide/security
11599
 
11600
 
11601
function ensureSafeMemberName(name, fullExpression) {
11602
  if (name === "__defineGetter__" || name === "__defineSetter__"
11603
      || name === "__lookupGetter__" || name === "__lookupSetter__"
11604
      || name === "__proto__") {
11605
    throw $parseMinErr('isecfld',
11606
        'Attempting to access a disallowed field in Angular expressions! '
11607
        + 'Expression: {0}', fullExpression);
11608
  }
11609
  return name;
11610
}
11611
 
11612
function ensureSafeObject(obj, fullExpression) {
11613
  // nifty check if obj is Function that is fast and works across iframes and other contexts
11614
  if (obj) {
11615
    if (obj.constructor === obj) {
11616
      throw $parseMinErr('isecfn',
11617
          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11618
          fullExpression);
11619
    } else if (// isWindow(obj)
11620
        obj.window === obj) {
11621
      throw $parseMinErr('isecwindow',
11622
          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
11623
          fullExpression);
11624
    } else if (// isElement(obj)
11625
        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
11626
      throw $parseMinErr('isecdom',
11627
          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
11628
          fullExpression);
11629
    } else if (// block Object so that we can't get hold of dangerous Object.* methods
11630
        obj === Object) {
11631
      throw $parseMinErr('isecobj',
11632
          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
11633
          fullExpression);
11634
    }
11635
  }
11636
  return obj;
11637
}
11638
 
11639
var CALL = Function.prototype.call;
11640
var APPLY = Function.prototype.apply;
11641
var BIND = Function.prototype.bind;
11642
 
11643
function ensureSafeFunction(obj, fullExpression) {
11644
  if (obj) {
11645
    if (obj.constructor === obj) {
11646
      throw $parseMinErr('isecfn',
11647
        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11648
        fullExpression);
11649
    } else if (obj === CALL || obj === APPLY || obj === BIND) {
11650
      throw $parseMinErr('isecff',
11651
        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
11652
        fullExpression);
11653
    }
11654
  }
11655
}
11656
 
11657
//Keyword constants
11658
var CONSTANTS = createMap();
11659
forEach({
11660
  'null': function() { return null; },
11661
  'true': function() { return true; },
11662
  'false': function() { return false; },
11663
  'undefined': function() {}
11664
}, function(constantGetter, name) {
11665
  constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
11666
  CONSTANTS[name] = constantGetter;
11667
});
11668
 
11669
//Not quite a constant, but can be lex/parsed the same
11670
CONSTANTS['this'] = function(self) { return self; };
11671
CONSTANTS['this'].sharedGetter = true;
11672
 
11673
 
11674
//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
11675
var OPERATORS = extend(createMap(), {
11676
    '+':function(self, locals, a, b) {
11677
      a=a(self, locals); b=b(self, locals);
11678
      if (isDefined(a)) {
11679
        if (isDefined(b)) {
11680
          return a + b;
11681
        }
11682
        return a;
11683
      }
11684
      return isDefined(b) ? b : undefined;},
11685
    '-':function(self, locals, a, b) {
11686
          a=a(self, locals); b=b(self, locals);
11687
          return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11688
        },
11689
    '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11690
    '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11691
    '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11692
    '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11693
    '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11694
    '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11695
    '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11696
    '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11697
    '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11698
    '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11699
    '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11700
    '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11701
    '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11702
    '!':function(self, locals, a) {return !a(self, locals);},
11703
 
11704
    //Tokenized as operators but parsed as assignment/filters
11705
    '=':true,
11706
    '|':true
11707
});
11708
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
11709
 
11710
 
11711
/////////////////////////////////////////
11712
 
11713
 
11714
/**
11715
 * @constructor
11716
 */
11717
var Lexer = function(options) {
11718
  this.options = options;
11719
};
11720
 
11721
Lexer.prototype = {
11722
  constructor: Lexer,
11723
 
11724
  lex: function(text) {
11725
    this.text = text;
11726
    this.index = 0;
11727
    this.tokens = [];
11728
 
11729
    while (this.index < this.text.length) {
11730
      var ch = this.text.charAt(this.index);
11731
      if (ch === '"' || ch === "'") {
11732
        this.readString(ch);
11733
      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11734
        this.readNumber();
11735
      } else if (this.isIdent(ch)) {
11736
        this.readIdent();
11737
      } else if (this.is(ch, '(){}[].,;:?')) {
11738
        this.tokens.push({index: this.index, text: ch});
11739
        this.index++;
11740
      } else if (this.isWhitespace(ch)) {
11741
        this.index++;
11742
      } else {
11743
        var ch2 = ch + this.peek();
11744
        var ch3 = ch2 + this.peek(2);
11745
        var op1 = OPERATORS[ch];
11746
        var op2 = OPERATORS[ch2];
11747
        var op3 = OPERATORS[ch3];
11748
        if (op1 || op2 || op3) {
11749
          var token = op3 ? ch3 : (op2 ? ch2 : ch);
11750
          this.tokens.push({index: this.index, text: token, operator: true});
11751
          this.index += token.length;
11752
        } else {
11753
          this.throwError('Unexpected next character ', this.index, this.index + 1);
11754
        }
11755
      }
11756
    }
11757
    return this.tokens;
11758
  },
11759
 
11760
  is: function(ch, chars) {
11761
    return chars.indexOf(ch) !== -1;
11762
  },
11763
 
11764
  peek: function(i) {
11765
    var num = i || 1;
11766
    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
11767
  },
11768
 
11769
  isNumber: function(ch) {
11770
    return ('0' <= ch && ch <= '9') && typeof ch === "string";
11771
  },
11772
 
11773
  isWhitespace: function(ch) {
11774
    // IE treats non-breaking space as \u00A0
11775
    return (ch === ' ' || ch === '\r' || ch === '\t' ||
11776
            ch === '\n' || ch === '\v' || ch === '\u00A0');
11777
  },
11778
 
11779
  isIdent: function(ch) {
11780
    return ('a' <= ch && ch <= 'z' ||
11781
            'A' <= ch && ch <= 'Z' ||
11782
            '_' === ch || ch === '$');
11783
  },
11784
 
11785
  isExpOperator: function(ch) {
11786
    return (ch === '-' || ch === '+' || this.isNumber(ch));
11787
  },
11788
 
11789
  throwError: function(error, start, end) {
11790
    end = end || this.index;
11791
    var colStr = (isDefined(start)
11792
            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
11793
            : ' ' + end);
11794
    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
11795
        error, colStr, this.text);
11796
  },
11797
 
11798
  readNumber: function() {
11799
    var number = '';
11800
    var start = this.index;
11801
    while (this.index < this.text.length) {
11802
      var ch = lowercase(this.text.charAt(this.index));
11803
      if (ch == '.' || this.isNumber(ch)) {
11804
        number += ch;
11805
      } else {
11806
        var peekCh = this.peek();
11807
        if (ch == 'e' && this.isExpOperator(peekCh)) {
11808
          number += ch;
11809
        } else if (this.isExpOperator(ch) &&
11810
            peekCh && this.isNumber(peekCh) &&
11811
            number.charAt(number.length - 1) == 'e') {
11812
          number += ch;
11813
        } else if (this.isExpOperator(ch) &&
11814
            (!peekCh || !this.isNumber(peekCh)) &&
11815
            number.charAt(number.length - 1) == 'e') {
11816
          this.throwError('Invalid exponent');
11817
        } else {
11818
          break;
11819
        }
11820
      }
11821
      this.index++;
11822
    }
11823
    this.tokens.push({
11824
      index: start,
11825
      text: number,
11826
      constant: true,
11827
      value: Number(number)
11828
    });
11829
  },
11830
 
11831
  readIdent: function() {
11832
    var start = this.index;
11833
    while (this.index < this.text.length) {
11834
      var ch = this.text.charAt(this.index);
11835
      if (!(this.isIdent(ch) || this.isNumber(ch))) {
11836
        break;
11837
      }
11838
      this.index++;
11839
    }
11840
    this.tokens.push({
11841
      index: start,
11842
      text: this.text.slice(start, this.index),
11843
      identifier: true
11844
    });
11845
  },
11846
 
11847
  readString: function(quote) {
11848
    var start = this.index;
11849
    this.index++;
11850
    var string = '';
11851
    var rawString = quote;
11852
    var escape = false;
11853
    while (this.index < this.text.length) {
11854
      var ch = this.text.charAt(this.index);
11855
      rawString += ch;
11856
      if (escape) {
11857
        if (ch === 'u') {
11858
          var hex = this.text.substring(this.index + 1, this.index + 5);
11859
          if (!hex.match(/[\da-f]{4}/i))
11860
            this.throwError('Invalid unicode escape [\\u' + hex + ']');
11861
          this.index += 4;
11862
          string += String.fromCharCode(parseInt(hex, 16));
11863
        } else {
11864
          var rep = ESCAPE[ch];
11865
          string = string + (rep || ch);
11866
        }
11867
        escape = false;
11868
      } else if (ch === '\\') {
11869
        escape = true;
11870
      } else if (ch === quote) {
11871
        this.index++;
11872
        this.tokens.push({
11873
          index: start,
11874
          text: rawString,
11875
          constant: true,
11876
          value: string
11877
        });
11878
        return;
11879
      } else {
11880
        string += ch;
11881
      }
11882
      this.index++;
11883
    }
11884
    this.throwError('Unterminated quote', start);
11885
  }
11886
};
11887
 
11888
 
11889
function isConstant(exp) {
11890
  return exp.constant;
11891
}
11892
 
11893
/**
11894
 * @constructor
11895
 */
11896
var Parser = function(lexer, $filter, options) {
11897
  this.lexer = lexer;
11898
  this.$filter = $filter;
11899
  this.options = options;
11900
};
11901
 
11902
Parser.ZERO = extend(function() {
11903
  return 0;
11904
}, {
11905
  sharedGetter: true,
11906
  constant: true
11907
});
11908
 
11909
Parser.prototype = {
11910
  constructor: Parser,
11911
 
11912
  parse: function(text) {
11913
    this.text = text;
11914
    this.tokens = this.lexer.lex(text);
11915
 
11916
    var value = this.statements();
11917
 
11918
    if (this.tokens.length !== 0) {
11919
      this.throwError('is an unexpected token', this.tokens[0]);
11920
    }
11921
 
11922
    value.literal = !!value.literal;
11923
    value.constant = !!value.constant;
11924
 
11925
    return value;
11926
  },
11927
 
11928
  primary: function() {
11929
    var primary;
11930
    if (this.expect('(')) {
11931
      primary = this.filterChain();
11932
      this.consume(')');
11933
    } else if (this.expect('[')) {
11934
      primary = this.arrayDeclaration();
11935
    } else if (this.expect('{')) {
11936
      primary = this.object();
11937
    } else if (this.peek().identifier) {
11938
      primary = this.identifier();
11939
    } else if (this.peek().constant) {
11940
      primary = this.constant();
11941
    } else {
11942
      this.throwError('not a primary expression', this.peek());
11943
    }
11944
 
11945
    var next, context;
11946
    while ((next = this.expect('(', '[', '.'))) {
11947
      if (next.text === '(') {
11948
        primary = this.functionCall(primary, context);
11949
        context = null;
11950
      } else if (next.text === '[') {
11951
        context = primary;
11952
        primary = this.objectIndex(primary);
11953
      } else if (next.text === '.') {
11954
        context = primary;
11955
        primary = this.fieldAccess(primary);
11956
      } else {
11957
        this.throwError('IMPOSSIBLE');
11958
      }
11959
    }
11960
    return primary;
11961
  },
11962
 
11963
  throwError: function(msg, token) {
11964
    throw $parseMinErr('syntax',
11965
        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
11966
          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
11967
  },
11968
 
11969
  peekToken: function() {
11970
    if (this.tokens.length === 0)
11971
      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
11972
    return this.tokens[0];
11973
  },
11974
 
11975
  peek: function(e1, e2, e3, e4) {
11976
    return this.peekAhead(0, e1, e2, e3, e4);
11977
  },
11978
  peekAhead: function(i, e1, e2, e3, e4) {
11979
    if (this.tokens.length > i) {
11980
      var token = this.tokens[i];
11981
      var t = token.text;
11982
      if (t === e1 || t === e2 || t === e3 || t === e4 ||
11983
          (!e1 && !e2 && !e3 && !e4)) {
11984
        return token;
11985
      }
11986
    }
11987
    return false;
11988
  },
11989
 
11990
  expect: function(e1, e2, e3, e4) {
11991
    var token = this.peek(e1, e2, e3, e4);
11992
    if (token) {
11993
      this.tokens.shift();
11994
      return token;
11995
    }
11996
    return false;
11997
  },
11998
 
11999
  consume: function(e1) {
12000
    if (this.tokens.length === 0) {
12001
      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12002
    }
12003
 
12004
    var token = this.expect(e1);
12005
    if (!token) {
12006
      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
12007
    }
12008
    return token;
12009
  },
12010
 
12011
  unaryFn: function(op, right) {
12012
    var fn = OPERATORS[op];
12013
    return extend(function $parseUnaryFn(self, locals) {
12014
      return fn(self, locals, right);
12015
    }, {
12016
      constant:right.constant,
12017
      inputs: [right]
12018
    });
12019
  },
12020
 
12021
  binaryFn: function(left, op, right, isBranching) {
12022
    var fn = OPERATORS[op];
12023
    return extend(function $parseBinaryFn(self, locals) {
12024
      return fn(self, locals, left, right);
12025
    }, {
12026
      constant: left.constant && right.constant,
12027
      inputs: !isBranching && [left, right]
12028
    });
12029
  },
12030
 
12031
  identifier: function() {
12032
    var id = this.consume().text;
12033
 
12034
    //Continue reading each `.identifier` unless it is a method invocation
12035
    while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
12036
      id += this.consume().text + this.consume().text;
12037
    }
12038
 
12039
    return CONSTANTS[id] || getterFn(id, this.options, this.text);
12040
  },
12041
 
12042
  constant: function() {
12043
    var value = this.consume().value;
12044
 
12045
    return extend(function $parseConstant() {
12046
      return value;
12047
    }, {
12048
      constant: true,
12049
      literal: true
12050
    });
12051
  },
12052
 
12053
  statements: function() {
12054
    var statements = [];
12055
    while (true) {
12056
      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12057
        statements.push(this.filterChain());
12058
      if (!this.expect(';')) {
12059
        // optimize for the common case where there is only one statement.
12060
        // TODO(size): maybe we should not support multiple statements?
12061
        return (statements.length === 1)
12062
            ? statements[0]
12063
            : function $parseStatements(self, locals) {
12064
                var value;
12065
                for (var i = 0, ii = statements.length; i < ii; i++) {
12066
                  value = statements[i](self, locals);
12067
                }
12068
                return value;
12069
              };
12070
      }
12071
    }
12072
  },
12073
 
12074
  filterChain: function() {
12075
    var left = this.expression();
12076
    var token;
12077
    while ((token = this.expect('|'))) {
12078
      left = this.filter(left);
12079
    }
12080
    return left;
12081
  },
12082
 
12083
  filter: function(inputFn) {
12084
    var fn = this.$filter(this.consume().text);
12085
    var argsFn;
12086
    var args;
12087
 
12088
    if (this.peek(':')) {
12089
      argsFn = [];
12090
      args = []; // we can safely reuse the array
12091
      while (this.expect(':')) {
12092
        argsFn.push(this.expression());
12093
      }
12094
    }
12095
 
12096
    var inputs = [inputFn].concat(argsFn || []);
12097
 
12098
    return extend(function $parseFilter(self, locals) {
12099
      var input = inputFn(self, locals);
12100
      if (args) {
12101
        args[0] = input;
12102
 
12103
        var i = argsFn.length;
12104
        while (i--) {
12105
          args[i + 1] = argsFn[i](self, locals);
12106
        }
12107
 
12108
        return fn.apply(undefined, args);
12109
      }
12110
 
12111
      return fn(input);
12112
    }, {
12113
      constant: !fn.$stateful && inputs.every(isConstant),
12114
      inputs: !fn.$stateful && inputs
12115
    });
12116
  },
12117
 
12118
  expression: function() {
12119
    return this.assignment();
12120
  },
12121
 
12122
  assignment: function() {
12123
    var left = this.ternary();
12124
    var right;
12125
    var token;
12126
    if ((token = this.expect('='))) {
12127
      if (!left.assign) {
12128
        this.throwError('implies assignment but [' +
12129
            this.text.substring(0, token.index) + '] can not be assigned to', token);
12130
      }
12131
      right = this.ternary();
12132
      return extend(function $parseAssignment(scope, locals) {
12133
        return left.assign(scope, right(scope, locals), locals);
12134
      }, {
12135
        inputs: [left, right]
12136
      });
12137
    }
12138
    return left;
12139
  },
12140
 
12141
  ternary: function() {
12142
    var left = this.logicalOR();
12143
    var middle;
12144
    var token;
12145
    if ((token = this.expect('?'))) {
12146
      middle = this.assignment();
12147
      if (this.consume(':')) {
12148
        var right = this.assignment();
12149
 
12150
        return extend(function $parseTernary(self, locals) {
12151
          return left(self, locals) ? middle(self, locals) : right(self, locals);
12152
        }, {
12153
          constant: left.constant && middle.constant && right.constant
12154
        });
12155
      }
12156
    }
12157
 
12158
    return left;
12159
  },
12160
 
12161
  logicalOR: function() {
12162
    var left = this.logicalAND();
12163
    var token;
12164
    while ((token = this.expect('||'))) {
12165
      left = this.binaryFn(left, token.text, this.logicalAND(), true);
12166
    }
12167
    return left;
12168
  },
12169
 
12170
  logicalAND: function() {
12171
    var left = this.equality();
12172
    var token;
12173
    while ((token = this.expect('&&'))) {
12174
      left = this.binaryFn(left, token.text, this.equality(), true);
12175
    }
12176
    return left;
12177
  },
12178
 
12179
  equality: function() {
12180
    var left = this.relational();
12181
    var token;
12182
    while ((token = this.expect('==','!=','===','!=='))) {
12183
      left = this.binaryFn(left, token.text, this.relational());
12184
    }
12185
    return left;
12186
  },
12187
 
12188
  relational: function() {
12189
    var left = this.additive();
12190
    var token;
12191
    while ((token = this.expect('<', '>', '<=', '>='))) {
12192
      left = this.binaryFn(left, token.text, this.additive());
12193
    }
12194
    return left;
12195
  },
12196
 
12197
  additive: function() {
12198
    var left = this.multiplicative();
12199
    var token;
12200
    while ((token = this.expect('+','-'))) {
12201
      left = this.binaryFn(left, token.text, this.multiplicative());
12202
    }
12203
    return left;
12204
  },
12205
 
12206
  multiplicative: function() {
12207
    var left = this.unary();
12208
    var token;
12209
    while ((token = this.expect('*','/','%'))) {
12210
      left = this.binaryFn(left, token.text, this.unary());
12211
    }
12212
    return left;
12213
  },
12214
 
12215
  unary: function() {
12216
    var token;
12217
    if (this.expect('+')) {
12218
      return this.primary();
12219
    } else if ((token = this.expect('-'))) {
12220
      return this.binaryFn(Parser.ZERO, token.text, this.unary());
12221
    } else if ((token = this.expect('!'))) {
12222
      return this.unaryFn(token.text, this.unary());
12223
    } else {
12224
      return this.primary();
12225
    }
12226
  },
12227
 
12228
  fieldAccess: function(object) {
12229
    var expression = this.text;
12230
    var field = this.consume().text;
12231
    var getter = getterFn(field, this.options, expression);
12232
 
12233
    return extend(function $parseFieldAccess(scope, locals, self) {
12234
      return getter(self || object(scope, locals));
12235
    }, {
12236
      assign: function(scope, value, locals) {
12237
        var o = object(scope, locals);
12238
        if (!o) object.assign(scope, o = {});
12239
        return setter(o, field, value, expression);
12240
      }
12241
    });
12242
  },
12243
 
12244
  objectIndex: function(obj) {
12245
    var expression = this.text;
12246
 
12247
    var indexFn = this.expression();
12248
    this.consume(']');
12249
 
12250
    return extend(function $parseObjectIndex(self, locals) {
12251
      var o = obj(self, locals),
12252
          i = indexFn(self, locals),
12253
          v;
12254
 
12255
      ensureSafeMemberName(i, expression);
12256
      if (!o) return undefined;
12257
      v = ensureSafeObject(o[i], expression);
12258
      return v;
12259
    }, {
12260
      assign: function(self, value, locals) {
12261
        var key = ensureSafeMemberName(indexFn(self, locals), expression);
12262
        // prevent overwriting of Function.constructor which would break ensureSafeObject check
12263
        var o = ensureSafeObject(obj(self, locals), expression);
12264
        if (!o) obj.assign(self, o = {});
12265
        return o[key] = value;
12266
      }
12267
    });
12268
  },
12269
 
12270
  functionCall: function(fnGetter, contextGetter) {
12271
    var argsFn = [];
12272
    if (this.peekToken().text !== ')') {
12273
      do {
12274
        argsFn.push(this.expression());
12275
      } while (this.expect(','));
12276
    }
12277
    this.consume(')');
12278
 
12279
    var expressionText = this.text;
12280
    // we can safely reuse the array across invocations
12281
    var args = argsFn.length ? [] : null;
12282
 
12283
    return function $parseFunctionCall(scope, locals) {
12284
      var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
12285
      var fn = fnGetter(scope, locals, context) || noop;
12286
 
12287
      if (args) {
12288
        var i = argsFn.length;
12289
        while (i--) {
12290
          args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
12291
        }
12292
      }
12293
 
12294
      ensureSafeObject(context, expressionText);
12295
      ensureSafeFunction(fn, expressionText);
12296
 
12297
      // IE doesn't have apply for some native functions
12298
      var v = fn.apply
12299
            ? fn.apply(context, args)
12300
            : fn(args[0], args[1], args[2], args[3], args[4]);
12301
 
12302
      return ensureSafeObject(v, expressionText);
12303
      };
12304
  },
12305
 
12306
  // This is used with json array declaration
12307
  arrayDeclaration: function() {
12308
    var elementFns = [];
12309
    if (this.peekToken().text !== ']') {
12310
      do {
12311
        if (this.peek(']')) {
12312
          // Support trailing commas per ES5.1.
12313
          break;
12314
        }
12315
        elementFns.push(this.expression());
12316
      } while (this.expect(','));
12317
    }
12318
    this.consume(']');
12319
 
12320
    return extend(function $parseArrayLiteral(self, locals) {
12321
      var array = [];
12322
      for (var i = 0, ii = elementFns.length; i < ii; i++) {
12323
        array.push(elementFns[i](self, locals));
12324
      }
12325
      return array;
12326
    }, {
12327
      literal: true,
12328
      constant: elementFns.every(isConstant),
12329
      inputs: elementFns
12330
    });
12331
  },
12332
 
12333
  object: function() {
12334
    var keys = [], valueFns = [];
12335
    if (this.peekToken().text !== '}') {
12336
      do {
12337
        if (this.peek('}')) {
12338
          // Support trailing commas per ES5.1.
12339
          break;
12340
        }
12341
        var token = this.consume();
12342
        if (token.constant) {
12343
          keys.push(token.value);
12344
        } else if (token.identifier) {
12345
          keys.push(token.text);
12346
        } else {
12347
          this.throwError("invalid key", token);
12348
        }
12349
        this.consume(':');
12350
        valueFns.push(this.expression());
12351
      } while (this.expect(','));
12352
    }
12353
    this.consume('}');
12354
 
12355
    return extend(function $parseObjectLiteral(self, locals) {
12356
      var object = {};
12357
      for (var i = 0, ii = valueFns.length; i < ii; i++) {
12358
        object[keys[i]] = valueFns[i](self, locals);
12359
      }
12360
      return object;
12361
    }, {
12362
      literal: true,
12363
      constant: valueFns.every(isConstant),
12364
      inputs: valueFns
12365
    });
12366
  }
12367
};
12368
 
12369
 
12370
//////////////////////////////////////////////////
12371
// Parser helper functions
12372
//////////////////////////////////////////////////
12373
 
12374
function setter(obj, path, setValue, fullExp) {
12375
  ensureSafeObject(obj, fullExp);
12376
 
12377
  var element = path.split('.'), key;
12378
  for (var i = 0; element.length > 1; i++) {
12379
    key = ensureSafeMemberName(element.shift(), fullExp);
12380
    var propertyObj = ensureSafeObject(obj[key], fullExp);
12381
    if (!propertyObj) {
12382
      propertyObj = {};
12383
      obj[key] = propertyObj;
12384
    }
12385
    obj = propertyObj;
12386
  }
12387
  key = ensureSafeMemberName(element.shift(), fullExp);
12388
  ensureSafeObject(obj[key], fullExp);
12389
  obj[key] = setValue;
12390
  return setValue;
12391
}
12392
 
12393
var getterFnCacheDefault = createMap();
12394
var getterFnCacheExpensive = createMap();
12395
 
12396
function isPossiblyDangerousMemberName(name) {
12397
  return name == 'constructor';
12398
}
12399
 
12400
/**
12401
 * Implementation of the "Black Hole" variant from:
12402
 * - http://jsperf.com/angularjs-parse-getter/4
12403
 * - http://jsperf.com/path-evaluation-simplified/7
12404
 */
12405
function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
12406
  ensureSafeMemberName(key0, fullExp);
12407
  ensureSafeMemberName(key1, fullExp);
12408
  ensureSafeMemberName(key2, fullExp);
12409
  ensureSafeMemberName(key3, fullExp);
12410
  ensureSafeMemberName(key4, fullExp);
12411
  var eso = function(o) {
12412
    return ensureSafeObject(o, fullExp);
12413
  };
12414
  var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
12415
  var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
12416
  var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
12417
  var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
12418
  var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
12419
 
12420
  return function cspSafeGetter(scope, locals) {
12421
    var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
12422
 
12423
    if (pathVal == null) return pathVal;
12424
    pathVal = eso0(pathVal[key0]);
12425
 
12426
    if (!key1) return pathVal;
12427
    if (pathVal == null) return undefined;
12428
    pathVal = eso1(pathVal[key1]);
12429
 
12430
    if (!key2) return pathVal;
12431
    if (pathVal == null) return undefined;
12432
    pathVal = eso2(pathVal[key2]);
12433
 
12434
    if (!key3) return pathVal;
12435
    if (pathVal == null) return undefined;
12436
    pathVal = eso3(pathVal[key3]);
12437
 
12438
    if (!key4) return pathVal;
12439
    if (pathVal == null) return undefined;
12440
    pathVal = eso4(pathVal[key4]);
12441
 
12442
    return pathVal;
12443
  };
12444
}
12445
 
12446
function getterFnWithEnsureSafeObject(fn, fullExpression) {
12447
  return function(s, l) {
12448
    return fn(s, l, ensureSafeObject, fullExpression);
12449
  };
12450
}
12451
 
12452
function getterFn(path, options, fullExp) {
12453
  var expensiveChecks = options.expensiveChecks;
12454
  var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
12455
  var fn = getterFnCache[path];
12456
  if (fn) return fn;
12457
 
12458
 
12459
  var pathKeys = path.split('.'),
12460
      pathKeysLength = pathKeys.length;
12461
 
12462
  // http://jsperf.com/angularjs-parse-getter/6
12463
  if (options.csp) {
12464
    if (pathKeysLength < 6) {
12465
      fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
12466
    } else {
12467
      fn = function cspSafeGetter(scope, locals) {
12468
        var i = 0, val;
12469
        do {
12470
          val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
12471
                                pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
12472
 
12473
          locals = undefined; // clear after first iteration
12474
          scope = val;
12475
        } while (i < pathKeysLength);
12476
        return val;
12477
      };
12478
    }
12479
  } else {
12480
    var code = '';
12481
    if (expensiveChecks) {
12482
      code += 's = eso(s, fe);\nl = eso(l, fe);\n';
12483
    }
12484
    var needsEnsureSafeObject = expensiveChecks;
12485
    forEach(pathKeys, function(key, index) {
12486
      ensureSafeMemberName(key, fullExp);
12487
      var lookupJs = (index
12488
                      // we simply dereference 's' on any .dot notation
12489
                      ? 's'
12490
                      // but if we are first then we check locals first, and if so read it first
12491
                      : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
12492
      if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
12493
        lookupJs = 'eso(' + lookupJs + ', fe)';
12494
        needsEnsureSafeObject = true;
12495
      }
12496
      code += 'if(s == null) return undefined;\n' +
12497
              's=' + lookupJs + ';\n';
12498
    });
12499
    code += 'return s;';
12500
 
12501
    /* jshint -W054 */
12502
    var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
12503
    /* jshint +W054 */
12504
    evaledFnGetter.toString = valueFn(code);
12505
    if (needsEnsureSafeObject) {
12506
      evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
12507
    }
12508
    fn = evaledFnGetter;
12509
  }
12510
 
12511
  fn.sharedGetter = true;
12512
  fn.assign = function(self, value) {
12513
    return setter(self, path, value, path);
12514
  };
12515
  getterFnCache[path] = fn;
12516
  return fn;
12517
}
12518
 
12519
var objectValueOf = Object.prototype.valueOf;
12520
 
12521
function getValueOf(value) {
12522
  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
12523
}
12524
 
12525
///////////////////////////////////
12526
 
12527
/**
12528
 * @ngdoc service
12529
 * @name $parse
12530
 * @kind function
12531
 *
12532
 * @description
12533
 *
12534
 * Converts Angular {@link guide/expression expression} into a function.
12535
 *
12536
 * ```js
12537
 *   var getter = $parse('user.name');
12538
 *   var setter = getter.assign;
12539
 *   var context = {user:{name:'angular'}};
12540
 *   var locals = {user:{name:'local'}};
12541
 *
12542
 *   expect(getter(context)).toEqual('angular');
12543
 *   setter(context, 'newValue');
12544
 *   expect(context.user.name).toEqual('newValue');
12545
 *   expect(getter(context, locals)).toEqual('local');
12546
 * ```
12547
 *
12548
 *
12549
 * @param {string} expression String expression to compile.
12550
 * @returns {function(context, locals)} a function which represents the compiled expression:
12551
 *
12552
 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
12553
 *      are evaluated against (typically a scope object).
12554
 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
12555
 *      `context`.
12556
 *
12557
 *    The returned function also has the following properties:
12558
 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
12559
 *        literal.
12560
 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
12561
 *        constant literals.
12562
 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
12563
 *        set to a function to change its value on the given context.
12564
 *
12565
 */
12566
 
12567
 
12568
/**
12569
 * @ngdoc provider
12570
 * @name $parseProvider
12571
 *
12572
 * @description
12573
 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
12574
 *  service.
12575
 */
12576
function $ParseProvider() {
12577
  var cacheDefault = createMap();
12578
  var cacheExpensive = createMap();
12579
 
12580
 
12581
 
12582
  this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
12583
    var $parseOptions = {
12584
          csp: $sniffer.csp,
12585
          expensiveChecks: false
12586
        },
12587
        $parseOptionsExpensive = {
12588
          csp: $sniffer.csp,
12589
          expensiveChecks: true
12590
        };
12591
 
12592
    function wrapSharedExpression(exp) {
12593
      var wrapped = exp;
12594
 
12595
      if (exp.sharedGetter) {
12596
        wrapped = function $parseWrapper(self, locals) {
12597
          return exp(self, locals);
12598
        };
12599
        wrapped.literal = exp.literal;
12600
        wrapped.constant = exp.constant;
12601
        wrapped.assign = exp.assign;
12602
      }
12603
 
12604
      return wrapped;
12605
    }
12606
 
12607
    return function $parse(exp, interceptorFn, expensiveChecks) {
12608
      var parsedExpression, oneTime, cacheKey;
12609
 
12610
      switch (typeof exp) {
12611
        case 'string':
12612
          cacheKey = exp = exp.trim();
12613
 
12614
          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
12615
          parsedExpression = cache[cacheKey];
12616
 
12617
          if (!parsedExpression) {
12618
            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
12619
              oneTime = true;
12620
              exp = exp.substring(2);
12621
            }
12622
 
12623
            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
12624
            var lexer = new Lexer(parseOptions);
12625
            var parser = new Parser(lexer, $filter, parseOptions);
12626
            parsedExpression = parser.parse(exp);
12627
 
12628
            if (parsedExpression.constant) {
12629
              parsedExpression.$$watchDelegate = constantWatchDelegate;
12630
            } else if (oneTime) {
12631
              //oneTime is not part of the exp passed to the Parser so we may have to
12632
              //wrap the parsedExpression before adding a $$watchDelegate
12633
              parsedExpression = wrapSharedExpression(parsedExpression);
12634
              parsedExpression.$$watchDelegate = parsedExpression.literal ?
12635
                oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
12636
            } else if (parsedExpression.inputs) {
12637
              parsedExpression.$$watchDelegate = inputsWatchDelegate;
12638
            }
12639
 
12640
            cache[cacheKey] = parsedExpression;
12641
          }
12642
          return addInterceptor(parsedExpression, interceptorFn);
12643
 
12644
        case 'function':
12645
          return addInterceptor(exp, interceptorFn);
12646
 
12647
        default:
12648
          return addInterceptor(noop, interceptorFn);
12649
      }
12650
    };
12651
 
12652
    function collectExpressionInputs(inputs, list) {
12653
      for (var i = 0, ii = inputs.length; i < ii; i++) {
12654
        var input = inputs[i];
12655
        if (!input.constant) {
12656
          if (input.inputs) {
12657
            collectExpressionInputs(input.inputs, list);
12658
          } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?
12659
            list.push(input);
12660
          }
12661
        }
12662
      }
12663
 
12664
      return list;
12665
    }
12666
 
12667
    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
12668
 
12669
      if (newValue == null || oldValueOfValue == null) { // null/undefined
12670
        return newValue === oldValueOfValue;
12671
      }
12672
 
12673
      if (typeof newValue === 'object') {
12674
 
12675
        // attempt to convert the value to a primitive type
12676
        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
12677
        //             be cheaply dirty-checked
12678
        newValue = getValueOf(newValue);
12679
 
12680
        if (typeof newValue === 'object') {
12681
          // objects/arrays are not supported - deep-watching them would be too expensive
12682
          return false;
12683
        }
12684
 
12685
        // fall-through to the primitive equality check
12686
      }
12687
 
12688
      //Primitive or NaN
12689
      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
12690
    }
12691
 
12692
    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12693
      var inputExpressions = parsedExpression.$$inputs ||
12694
                    (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));
12695
 
12696
      var lastResult;
12697
 
12698
      if (inputExpressions.length === 1) {
12699
        var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails
12700
        inputExpressions = inputExpressions[0];
12701
        return scope.$watch(function expressionInputWatch(scope) {
12702
          var newInputValue = inputExpressions(scope);
12703
          if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
12704
            lastResult = parsedExpression(scope);
12705
            oldInputValue = newInputValue && getValueOf(newInputValue);
12706
          }
12707
          return lastResult;
12708
        }, listener, objectEquality);
12709
      }
12710
 
12711
      var oldInputValueOfValues = [];
12712
      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12713
        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
12714
      }
12715
 
12716
      return scope.$watch(function expressionInputsWatch(scope) {
12717
        var changed = false;
12718
 
12719
        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12720
          var newInputValue = inputExpressions[i](scope);
12721
          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
12722
            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
12723
          }
12724
        }
12725
 
12726
        if (changed) {
12727
          lastResult = parsedExpression(scope);
12728
        }
12729
 
12730
        return lastResult;
12731
      }, listener, objectEquality);
12732
    }
12733
 
12734
    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12735
      var unwatch, lastValue;
12736
      return unwatch = scope.$watch(function oneTimeWatch(scope) {
12737
        return parsedExpression(scope);
12738
      }, function oneTimeListener(value, old, scope) {
12739
        lastValue = value;
12740
        if (isFunction(listener)) {
12741
          listener.apply(this, arguments);
12742
        }
12743
        if (isDefined(value)) {
12744
          scope.$$postDigest(function() {
12745
            if (isDefined(lastValue)) {
12746
              unwatch();
12747
            }
12748
          });
12749
        }
12750
      }, objectEquality);
12751
    }
12752
 
12753
    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12754
      var unwatch, lastValue;
12755
      return unwatch = scope.$watch(function oneTimeWatch(scope) {
12756
        return parsedExpression(scope);
12757
      }, function oneTimeListener(value, old, scope) {
12758
        lastValue = value;
12759
        if (isFunction(listener)) {
12760
          listener.call(this, value, old, scope);
12761
        }
12762
        if (isAllDefined(value)) {
12763
          scope.$$postDigest(function() {
12764
            if (isAllDefined(lastValue)) unwatch();
12765
          });
12766
        }
12767
      }, objectEquality);
12768
 
12769
      function isAllDefined(value) {
12770
        var allDefined = true;
12771
        forEach(value, function(val) {
12772
          if (!isDefined(val)) allDefined = false;
12773
        });
12774
        return allDefined;
12775
      }
12776
    }
12777
 
12778
    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12779
      var unwatch;
12780
      return unwatch = scope.$watch(function constantWatch(scope) {
12781
        return parsedExpression(scope);
12782
      }, function constantListener(value, old, scope) {
12783
        if (isFunction(listener)) {
12784
          listener.apply(this, arguments);
12785
        }
12786
        unwatch();
12787
      }, objectEquality);
12788
    }
12789
 
12790
    function addInterceptor(parsedExpression, interceptorFn) {
12791
      if (!interceptorFn) return parsedExpression;
12792
      var watchDelegate = parsedExpression.$$watchDelegate;
12793
 
12794
      var regularWatch =
12795
          watchDelegate !== oneTimeLiteralWatchDelegate &&
12796
          watchDelegate !== oneTimeWatchDelegate;
12797
 
12798
      var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12799
        var value = parsedExpression(scope, locals);
12800
        return interceptorFn(value, scope, locals);
12801
      } : function oneTimeInterceptedExpression(scope, locals) {
12802
        var value = parsedExpression(scope, locals);
12803
        var result = interceptorFn(value, scope, locals);
12804
        // we only return the interceptor's result if the
12805
        // initial value is defined (for bind-once)
12806
        return isDefined(value) ? result : value;
12807
      };
12808
 
12809
      // Propagate $$watchDelegates other then inputsWatchDelegate
12810
      if (parsedExpression.$$watchDelegate &&
12811
          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
12812
        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
12813
      } else if (!interceptorFn.$stateful) {
12814
        // If there is an interceptor, but no watchDelegate then treat the interceptor like
12815
        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
12816
        fn.$$watchDelegate = inputsWatchDelegate;
12817
        fn.inputs = [parsedExpression];
12818
      }
12819
 
12820
      return fn;
12821
    }
12822
  }];
12823
}
12824
 
12825
/**
12826
 * @ngdoc service
12827
 * @name $q
12828
 * @requires $rootScope
12829
 *
12830
 * @description
12831
 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
12832
 * when they are done processing.
12833
 *
12834
 * This is an implementation of promises/deferred objects inspired by
12835
 * [Kris Kowal's Q](https://github.com/kriskowal/q).
12836
 *
12837
 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
12838
 * implementations, and the other which resembles ES6 promises to some degree.
12839
 *
12840
 * # $q constructor
12841
 *
12842
 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
12843
 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
12844
 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
12845
 *
12846
 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
12847
 * available yet.
12848
 *
12849
 * It can be used like so:
12850
 *
12851
 * ```js
12852
 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12853
 *   // are available in the current lexical scope (they could have been injected or passed in).
12854
 *
12855
 *   function asyncGreet(name) {
12856
 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
12857
 *     return $q(function(resolve, reject) {
12858
 *       setTimeout(function() {
12859
 *         if (okToGreet(name)) {
12860
 *           resolve('Hello, ' + name + '!');
12861
 *         } else {
12862
 *           reject('Greeting ' + name + ' is not allowed.');
12863
 *         }
12864
 *       }, 1000);
12865
 *     });
12866
 *   }
12867
 *
12868
 *   var promise = asyncGreet('Robin Hood');
12869
 *   promise.then(function(greeting) {
12870
 *     alert('Success: ' + greeting);
12871
 *   }, function(reason) {
12872
 *     alert('Failed: ' + reason);
12873
 *   });
12874
 * ```
12875
 *
12876
 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
12877
 *
12878
 * However, the more traditional CommonJS-style usage is still available, and documented below.
12879
 *
12880
 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
12881
 * interface for interacting with an object that represents the result of an action that is
12882
 * performed asynchronously, and may or may not be finished at any given point in time.
12883
 *
12884
 * From the perspective of dealing with error handling, deferred and promise APIs are to
12885
 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
12886
 *
12887
 * ```js
12888
 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12889
 *   // are available in the current lexical scope (they could have been injected or passed in).
12890
 *
12891
 *   function asyncGreet(name) {
12892
 *     var deferred = $q.defer();
12893
 *
12894
 *     setTimeout(function() {
12895
 *       deferred.notify('About to greet ' + name + '.');
12896
 *
12897
 *       if (okToGreet(name)) {
12898
 *         deferred.resolve('Hello, ' + name + '!');
12899
 *       } else {
12900
 *         deferred.reject('Greeting ' + name + ' is not allowed.');
12901
 *       }
12902
 *     }, 1000);
12903
 *
12904
 *     return deferred.promise;
12905
 *   }
12906
 *
12907
 *   var promise = asyncGreet('Robin Hood');
12908
 *   promise.then(function(greeting) {
12909
 *     alert('Success: ' + greeting);
12910
 *   }, function(reason) {
12911
 *     alert('Failed: ' + reason);
12912
 *   }, function(update) {
12913
 *     alert('Got notification: ' + update);
12914
 *   });
12915
 * ```
12916
 *
12917
 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
12918
 * comes in the way of guarantees that promise and deferred APIs make, see
12919
 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
12920
 *
12921
 * Additionally the promise api allows for composition that is very hard to do with the
12922
 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
12923
 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
12924
 * section on serial or parallel joining of promises.
12925
 *
12926
 * # The Deferred API
12927
 *
12928
 * A new instance of deferred is constructed by calling `$q.defer()`.
12929
 *
12930
 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
12931
 * that can be used for signaling the successful or unsuccessful completion, as well as the status
12932
 * of the task.
12933
 *
12934
 * **Methods**
12935
 *
12936
 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
12937
 *   constructed via `$q.reject`, the promise will be rejected instead.
12938
 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
12939
 *   resolving it with a rejection constructed via `$q.reject`.
12940
 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
12941
 *   multiple times before the promise is either resolved or rejected.
12942
 *
12943
 * **Properties**
12944
 *
12945
 * - promise – `{Promise}` – promise object associated with this deferred.
12946
 *
12947
 *
12948
 * # The Promise API
12949
 *
12950
 * A new promise instance is created when a deferred instance is created and can be retrieved by
12951
 * calling `deferred.promise`.
12952
 *
12953
 * The purpose of the promise object is to allow for interested parties to get access to the result
12954
 * of the deferred task when it completes.
12955
 *
12956
 * **Methods**
12957
 *
12958
 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
12959
 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
12960
 *   as soon as the result is available. The callbacks are called with a single argument: the result
12961
 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
12962
 *   provide a progress indication, before the promise is resolved or rejected.
12963
 *
12964
 *   This method *returns a new promise* which is resolved or rejected via the return value of the
12965
 *   `successCallback`, `errorCallback`. It also notifies via the return value of the
12966
 *   `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback
12967
 *   method.
12968
 *
12969
 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
12970
 *
12971
 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
12972
 *   but to do so without modifying the final value. This is useful to release resources or do some
12973
 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
12974
 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
12975
 *   more information.
12976
 *
12977
 * # Chaining promises
12978
 *
12979
 * Because calling the `then` method of a promise returns a new derived promise, it is easily
12980
 * possible to create a chain of promises:
12981
 *
12982
 * ```js
12983
 *   promiseB = promiseA.then(function(result) {
12984
 *     return result + 1;
12985
 *   });
12986
 *
12987
 *   // promiseB will be resolved immediately after promiseA is resolved and its value
12988
 *   // will be the result of promiseA incremented by 1
12989
 * ```
12990
 *
12991
 * It is possible to create chains of any length and since a promise can be resolved with another
12992
 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
12993
 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
12994
 * $http's response interceptors.
12995
 *
12996
 *
12997
 * # Differences between Kris Kowal's Q and $q
12998
 *
12999
 *  There are two main differences:
13000
 *
13001
 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
13002
 *   mechanism in angular, which means faster propagation of resolution or rejection into your
13003
 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
13004
 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
13005
 *   all the important functionality needed for common async tasks.
13006
 *
13007
 *  # Testing
13008
 *
13009
 *  ```js
13010
 *    it('should simulate promise', inject(function($q, $rootScope) {
13011
 *      var deferred = $q.defer();
13012
 *      var promise = deferred.promise;
13013
 *      var resolvedValue;
13014
 *
13015
 *      promise.then(function(value) { resolvedValue = value; });
13016
 *      expect(resolvedValue).toBeUndefined();
13017
 *
13018
 *      // Simulate resolving of promise
13019
 *      deferred.resolve(123);
13020
 *      // Note that the 'then' function does not get called synchronously.
13021
 *      // This is because we want the promise API to always be async, whether or not
13022
 *      // it got called synchronously or asynchronously.
13023
 *      expect(resolvedValue).toBeUndefined();
13024
 *
13025
 *      // Propagate promise resolution to 'then' functions using $apply().
13026
 *      $rootScope.$apply();
13027
 *      expect(resolvedValue).toEqual(123);
13028
 *    }));
13029
 *  ```
13030
 *
13031
 * @param {function(function, function)} resolver Function which is responsible for resolving or
13032
 *   rejecting the newly created promise. The first parameter is a function which resolves the
13033
 *   promise, the second parameter is a function which rejects the promise.
13034
 *
13035
 * @returns {Promise} The newly created promise.
13036
 */
13037
function $QProvider() {
13038
 
13039
  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
13040
    return qFactory(function(callback) {
13041
      $rootScope.$evalAsync(callback);
13042
    }, $exceptionHandler);
13043
  }];
13044
}
13045
 
13046
function $$QProvider() {
13047
  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
13048
    return qFactory(function(callback) {
13049
      $browser.defer(callback);
13050
    }, $exceptionHandler);
13051
  }];
13052
}
13053
 
13054
/**
13055
 * Constructs a promise manager.
13056
 *
13057
 * @param {function(function)} nextTick Function for executing functions in the next turn.
13058
 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
13059
 *     debugging purposes.
13060
 * @returns {object} Promise manager.
13061
 */
13062
function qFactory(nextTick, exceptionHandler) {
13063
  var $qMinErr = minErr('$q', TypeError);
13064
  function callOnce(self, resolveFn, rejectFn) {
13065
    var called = false;
13066
    function wrap(fn) {
13067
      return function(value) {
13068
        if (called) return;
13069
        called = true;
13070
        fn.call(self, value);
13071
      };
13072
    }
13073
 
13074
    return [wrap(resolveFn), wrap(rejectFn)];
13075
  }
13076
 
13077
  /**
13078
   * @ngdoc method
13079
   * @name ng.$q#defer
13080
   * @kind function
13081
   *
13082
   * @description
13083
   * Creates a `Deferred` object which represents a task which will finish in the future.
13084
   *
13085
   * @returns {Deferred} Returns a new instance of deferred.
13086
   */
13087
  var defer = function() {
13088
    return new Deferred();
13089
  };
13090
 
13091
  function Promise() {
13092
    this.$$state = { status: 0 };
13093
  }
13094
 
13095
  Promise.prototype = {
13096
    then: function(onFulfilled, onRejected, progressBack) {
13097
      var result = new Deferred();
13098
 
13099
      this.$$state.pending = this.$$state.pending || [];
13100
      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
13101
      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
13102
 
13103
      return result.promise;
13104
    },
13105
 
13106
    "catch": function(callback) {
13107
      return this.then(null, callback);
13108
    },
13109
 
13110
    "finally": function(callback, progressBack) {
13111
      return this.then(function(value) {
13112
        return handleCallback(value, true, callback);
13113
      }, function(error) {
13114
        return handleCallback(error, false, callback);
13115
      }, progressBack);
13116
    }
13117
  };
13118
 
13119
  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
13120
  function simpleBind(context, fn) {
13121
    return function(value) {
13122
      fn.call(context, value);
13123
    };
13124
  }
13125
 
13126
  function processQueue(state) {
13127
    var fn, promise, pending;
13128
 
13129
    pending = state.pending;
13130
    state.processScheduled = false;
13131
    state.pending = undefined;
13132
    for (var i = 0, ii = pending.length; i < ii; ++i) {
13133
      promise = pending[i][0];
13134
      fn = pending[i][state.status];
13135
      try {
13136
        if (isFunction(fn)) {
13137
          promise.resolve(fn(state.value));
13138
        } else if (state.status === 1) {
13139
          promise.resolve(state.value);
13140
        } else {
13141
          promise.reject(state.value);
13142
        }
13143
      } catch (e) {
13144
        promise.reject(e);
13145
        exceptionHandler(e);
13146
      }
13147
    }
13148
  }
13149
 
13150
  function scheduleProcessQueue(state) {
13151
    if (state.processScheduled || !state.pending) return;
13152
    state.processScheduled = true;
13153
    nextTick(function() { processQueue(state); });
13154
  }
13155
 
13156
  function Deferred() {
13157
    this.promise = new Promise();
13158
    //Necessary to support unbound execution :/
13159
    this.resolve = simpleBind(this, this.resolve);
13160
    this.reject = simpleBind(this, this.reject);
13161
    this.notify = simpleBind(this, this.notify);
13162
  }
13163
 
13164
  Deferred.prototype = {
13165
    resolve: function(val) {
13166
      if (this.promise.$$state.status) return;
13167
      if (val === this.promise) {
13168
        this.$$reject($qMinErr(
13169
          'qcycle',
13170
          "Expected promise to be resolved with value other than itself '{0}'",
13171
          val));
13172
      }
13173
      else {
13174
        this.$$resolve(val);
13175
      }
13176
 
13177
    },
13178
 
13179
    $$resolve: function(val) {
13180
      var then, fns;
13181
 
13182
      fns = callOnce(this, this.$$resolve, this.$$reject);
13183
      try {
13184
        if ((isObject(val) || isFunction(val))) then = val && val.then;
13185
        if (isFunction(then)) {
13186
          this.promise.$$state.status = -1;
13187
          then.call(val, fns[0], fns[1], this.notify);
13188
        } else {
13189
          this.promise.$$state.value = val;
13190
          this.promise.$$state.status = 1;
13191
          scheduleProcessQueue(this.promise.$$state);
13192
        }
13193
      } catch (e) {
13194
        fns[1](e);
13195
        exceptionHandler(e);
13196
      }
13197
    },
13198
 
13199
    reject: function(reason) {
13200
      if (this.promise.$$state.status) return;
13201
      this.$$reject(reason);
13202
    },
13203
 
13204
    $$reject: function(reason) {
13205
      this.promise.$$state.value = reason;
13206
      this.promise.$$state.status = 2;
13207
      scheduleProcessQueue(this.promise.$$state);
13208
    },
13209
 
13210
    notify: function(progress) {
13211
      var callbacks = this.promise.$$state.pending;
13212
 
13213
      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
13214
        nextTick(function() {
13215
          var callback, result;
13216
          for (var i = 0, ii = callbacks.length; i < ii; i++) {
13217
            result = callbacks[i][0];
13218
            callback = callbacks[i][3];
13219
            try {
13220
              result.notify(isFunction(callback) ? callback(progress) : progress);
13221
            } catch (e) {
13222
              exceptionHandler(e);
13223
            }
13224
          }
13225
        });
13226
      }
13227
    }
13228
  };
13229
 
13230
  /**
13231
   * @ngdoc method
13232
   * @name $q#reject
13233
   * @kind function
13234
   *
13235
   * @description
13236
   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
13237
   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
13238
   * a promise chain, you don't need to worry about it.
13239
   *
13240
   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
13241
   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
13242
   * a promise error callback and you want to forward the error to the promise derived from the
13243
   * current promise, you have to "rethrow" the error by returning a rejection constructed via
13244
   * `reject`.
13245
   *
13246
   * ```js
13247
   *   promiseB = promiseA.then(function(result) {
13248
   *     // success: do something and resolve promiseB
13249
   *     //          with the old or a new result
13250
   *     return result;
13251
   *   }, function(reason) {
13252
   *     // error: handle the error if possible and
13253
   *     //        resolve promiseB with newPromiseOrValue,
13254
   *     //        otherwise forward the rejection to promiseB
13255
   *     if (canHandle(reason)) {
13256
   *      // handle the error and recover
13257
   *      return newPromiseOrValue;
13258
   *     }
13259
   *     return $q.reject(reason);
13260
   *   });
13261
   * ```
13262
   *
13263
   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
13264
   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
13265
   */
13266
  var reject = function(reason) {
13267
    var result = new Deferred();
13268
    result.reject(reason);
13269
    return result.promise;
13270
  };
13271
 
13272
  var makePromise = function makePromise(value, resolved) {
13273
    var result = new Deferred();
13274
    if (resolved) {
13275
      result.resolve(value);
13276
    } else {
13277
      result.reject(value);
13278
    }
13279
    return result.promise;
13280
  };
13281
 
13282
  var handleCallback = function handleCallback(value, isResolved, callback) {
13283
    var callbackOutput = null;
13284
    try {
13285
      if (isFunction(callback)) callbackOutput = callback();
13286
    } catch (e) {
13287
      return makePromise(e, false);
13288
    }
13289
    if (isPromiseLike(callbackOutput)) {
13290
      return callbackOutput.then(function() {
13291
        return makePromise(value, isResolved);
13292
      }, function(error) {
13293
        return makePromise(error, false);
13294
      });
13295
    } else {
13296
      return makePromise(value, isResolved);
13297
    }
13298
  };
13299
 
13300
  /**
13301
   * @ngdoc method
13302
   * @name $q#when
13303
   * @kind function
13304
   *
13305
   * @description
13306
   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
13307
   * This is useful when you are dealing with an object that might or might not be a promise, or if
13308
   * the promise comes from a source that can't be trusted.
13309
   *
13310
   * @param {*} value Value or a promise
13311
   * @returns {Promise} Returns a promise of the passed value or promise
13312
   */
13313
 
13314
 
13315
  var when = function(value, callback, errback, progressBack) {
13316
    var result = new Deferred();
13317
    result.resolve(value);
13318
    return result.promise.then(callback, errback, progressBack);
13319
  };
13320
 
13321
  /**
13322
   * @ngdoc method
13323
   * @name $q#all
13324
   * @kind function
13325
   *
13326
   * @description
13327
   * Combines multiple promises into a single promise that is resolved when all of the input
13328
   * promises are resolved.
13329
   *
13330
   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
13331
   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
13332
   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
13333
   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
13334
   *   with the same rejection value.
13335
   */
13336
 
13337
  function all(promises) {
13338
    var deferred = new Deferred(),
13339
        counter = 0,
13340
        results = isArray(promises) ? [] : {};
13341
 
13342
    forEach(promises, function(promise, key) {
13343
      counter++;
13344
      when(promise).then(function(value) {
13345
        if (results.hasOwnProperty(key)) return;
13346
        results[key] = value;
13347
        if (!(--counter)) deferred.resolve(results);
13348
      }, function(reason) {
13349
        if (results.hasOwnProperty(key)) return;
13350
        deferred.reject(reason);
13351
      });
13352
    });
13353
 
13354
    if (counter === 0) {
13355
      deferred.resolve(results);
13356
    }
13357
 
13358
    return deferred.promise;
13359
  }
13360
 
13361
  var $Q = function Q(resolver) {
13362
    if (!isFunction(resolver)) {
13363
      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
13364
    }
13365
 
13366
    if (!(this instanceof Q)) {
13367
      // More useful when $Q is the Promise itself.
13368
      return new Q(resolver);
13369
    }
13370
 
13371
    var deferred = new Deferred();
13372
 
13373
    function resolveFn(value) {
13374
      deferred.resolve(value);
13375
    }
13376
 
13377
    function rejectFn(reason) {
13378
      deferred.reject(reason);
13379
    }
13380
 
13381
    resolver(resolveFn, rejectFn);
13382
 
13383
    return deferred.promise;
13384
  };
13385
 
13386
  $Q.defer = defer;
13387
  $Q.reject = reject;
13388
  $Q.when = when;
13389
  $Q.all = all;
13390
 
13391
  return $Q;
13392
}
13393
 
13394
function $$RAFProvider() { //rAF
13395
  this.$get = ['$window', '$timeout', function($window, $timeout) {
13396
    var requestAnimationFrame = $window.requestAnimationFrame ||
13397
                                $window.webkitRequestAnimationFrame ||
13398
                                $window.mozRequestAnimationFrame;
13399
 
13400
    var cancelAnimationFrame = $window.cancelAnimationFrame ||
13401
                               $window.webkitCancelAnimationFrame ||
13402
                               $window.mozCancelAnimationFrame ||
13403
                               $window.webkitCancelRequestAnimationFrame;
13404
 
13405
    var rafSupported = !!requestAnimationFrame;
13406
    var raf = rafSupported
13407
      ? function(fn) {
13408
          var id = requestAnimationFrame(fn);
13409
          return function() {
13410
            cancelAnimationFrame(id);
13411
          };
13412
        }
13413
      : function(fn) {
13414
          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
13415
          return function() {
13416
            $timeout.cancel(timer);
13417
          };
13418
        };
13419
 
13420
    raf.supported = rafSupported;
13421
 
13422
    return raf;
13423
  }];
13424
}
13425
 
13426
/**
13427
 * DESIGN NOTES
13428
 *
13429
 * The design decisions behind the scope are heavily favored for speed and memory consumption.
13430
 *
13431
 * The typical use of scope is to watch the expressions, which most of the time return the same
13432
 * value as last time so we optimize the operation.
13433
 *
13434
 * Closures construction is expensive in terms of speed as well as memory:
13435
 *   - No closures, instead use prototypical inheritance for API
13436
 *   - Internal state needs to be stored on scope directly, which means that private state is
13437
 *     exposed as $$____ properties
13438
 *
13439
 * Loop operations are optimized by using while(count--) { ... }
13440
 *   - this means that in order to keep the same order of execution as addition we have to add
13441
 *     items to the array at the beginning (unshift) instead of at the end (push)
13442
 *
13443
 * Child scopes are created and removed often
13444
 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
13445
 *
13446
 * There are few watches then a lot of observers. This is why you don't want the observer to be
13447
 * implemented in the same way as watch. Watch requires return of initialization function which
13448
 * are expensive to construct.
13449
 */
13450
 
13451
 
13452
/**
13453
 * @ngdoc provider
13454
 * @name $rootScopeProvider
13455
 * @description
13456
 *
13457
 * Provider for the $rootScope service.
13458
 */
13459
 
13460
/**
13461
 * @ngdoc method
13462
 * @name $rootScopeProvider#digestTtl
13463
 * @description
13464
 *
13465
 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
13466
 * assuming that the model is unstable.
13467
 *
13468
 * The current default is 10 iterations.
13469
 *
13470
 * In complex applications it's possible that the dependencies between `$watch`s will result in
13471
 * several digest iterations. However if an application needs more than the default 10 digest
13472
 * iterations for its model to stabilize then you should investigate what is causing the model to
13473
 * continuously change during the digest.
13474
 *
13475
 * Increasing the TTL could have performance implications, so you should not change it without
13476
 * proper justification.
13477
 *
13478
 * @param {number} limit The number of digest iterations.
13479
 */
13480
 
13481
 
13482
/**
13483
 * @ngdoc service
13484
 * @name $rootScope
13485
 * @description
13486
 *
13487
 * Every application has a single root {@link ng.$rootScope.Scope scope}.
13488
 * All other scopes are descendant scopes of the root scope. Scopes provide separation
13489
 * between the model and the view, via a mechanism for watching the model for changes.
13490
 * They also provide an event emission/broadcast and subscription facility. See the
13491
 * {@link guide/scope developer guide on scopes}.
13492
 */
13493
function $RootScopeProvider() {
13494
  var TTL = 10;
13495
  var $rootScopeMinErr = minErr('$rootScope');
13496
  var lastDirtyWatch = null;
13497
  var applyAsyncId = null;
13498
 
13499
  this.digestTtl = function(value) {
13500
    if (arguments.length) {
13501
      TTL = value;
13502
    }
13503
    return TTL;
13504
  };
13505
 
13506
  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
13507
      function($injector, $exceptionHandler, $parse, $browser) {
13508
 
13509
    /**
13510
     * @ngdoc type
13511
     * @name $rootScope.Scope
13512
     *
13513
     * @description
13514
     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
13515
     * {@link auto.$injector $injector}. Child scopes are created using the
13516
     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
13517
     * compiled HTML template is executed.)
13518
     *
13519
     * Here is a simple scope snippet to show how you can interact with the scope.
13520
     * ```html
13521
     * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
13522
     * ```
13523
     *
13524
     * # Inheritance
13525
     * A scope can inherit from a parent scope, as in this example:
13526
     * ```js
13527
         var parent = $rootScope;
13528
         var child = parent.$new();
13529
 
13530
         parent.salutation = "Hello";
13531
         child.name = "World";
13532
         expect(child.salutation).toEqual('Hello');
13533
 
13534
         child.salutation = "Welcome";
13535
         expect(child.salutation).toEqual('Welcome');
13536
         expect(parent.salutation).toEqual('Hello');
13537
     * ```
13538
     *
13539
     * When interacting with `Scope` in tests, additional helper methods are available on the
13540
     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
13541
     * details.
13542
     *
13543
     *
13544
     * @param {Object.<string, function()>=} providers Map of service factory which need to be
13545
     *                                       provided for the current scope. Defaults to {@link ng}.
13546
     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
13547
     *                              append/override services provided by `providers`. This is handy
13548
     *                              when unit-testing and having the need to override a default
13549
     *                              service.
13550
     * @returns {Object} Newly created scope.
13551
     *
13552
     */
13553
    function Scope() {
13554
      this.$id = nextUid();
13555
      this.$$phase = this.$parent = this.$$watchers =
13556
                     this.$$nextSibling = this.$$prevSibling =
13557
                     this.$$childHead = this.$$childTail = null;
13558
      this.$root = this;
13559
      this.$$destroyed = false;
13560
      this.$$listeners = {};
13561
      this.$$listenerCount = {};
13562
      this.$$isolateBindings = null;
13563
    }
13564
 
13565
    /**
13566
     * @ngdoc property
13567
     * @name $rootScope.Scope#$id
13568
     *
13569
     * @description
13570
     * Unique scope ID (monotonically increasing) useful for debugging.
13571
     */
13572
 
13573
     /**
13574
      * @ngdoc property
13575
      * @name $rootScope.Scope#$parent
13576
      *
13577
      * @description
13578
      * Reference to the parent scope.
13579
      */
13580
 
13581
      /**
13582
       * @ngdoc property
13583
       * @name $rootScope.Scope#$root
13584
       *
13585
       * @description
13586
       * Reference to the root scope.
13587
       */
13588
 
13589
    Scope.prototype = {
13590
      constructor: Scope,
13591
      /**
13592
       * @ngdoc method
13593
       * @name $rootScope.Scope#$new
13594
       * @kind function
13595
       *
13596
       * @description
13597
       * Creates a new child {@link ng.$rootScope.Scope scope}.
13598
       *
13599
       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
13600
       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
13601
       *
13602
       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
13603
       * desired for the scope and its child scopes to be permanently detached from the parent and
13604
       * thus stop participating in model change detection and listener notification by invoking.
13605
       *
13606
       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
13607
       *         parent scope. The scope is isolated, as it can not see parent scope properties.
13608
       *         When creating widgets, it is useful for the widget to not accidentally read parent
13609
       *         state.
13610
       *
13611
       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
13612
       *                              of the newly created scope. Defaults to `this` scope if not provided.
13613
       *                              This is used when creating a transclude scope to correctly place it
13614
       *                              in the scope hierarchy while maintaining the correct prototypical
13615
       *                              inheritance.
13616
       *
13617
       * @returns {Object} The newly created child scope.
13618
       *
13619
       */
13620
      $new: function(isolate, parent) {
13621
        var child;
13622
 
13623
        parent = parent || this;
13624
 
13625
        if (isolate) {
13626
          child = new Scope();
13627
          child.$root = this.$root;
13628
        } else {
13629
          // Only create a child scope class if somebody asks for one,
13630
          // but cache it to allow the VM to optimize lookups.
13631
          if (!this.$$ChildScope) {
13632
            this.$$ChildScope = function ChildScope() {
13633
              this.$$watchers = this.$$nextSibling =
13634
                  this.$$childHead = this.$$childTail = null;
13635
              this.$$listeners = {};
13636
              this.$$listenerCount = {};
13637
              this.$id = nextUid();
13638
              this.$$ChildScope = null;
13639
            };
13640
            this.$$ChildScope.prototype = this;
13641
          }
13642
          child = new this.$$ChildScope();
13643
        }
13644
        child.$parent = parent;
13645
        child.$$prevSibling = parent.$$childTail;
13646
        if (parent.$$childHead) {
13647
          parent.$$childTail.$$nextSibling = child;
13648
          parent.$$childTail = child;
13649
        } else {
13650
          parent.$$childHead = parent.$$childTail = child;
13651
        }
13652
 
13653
        // When the new scope is not isolated or we inherit from `this`, and
13654
        // the parent scope is destroyed, the property `$$destroyed` is inherited
13655
        // prototypically. In all other cases, this property needs to be set
13656
        // when the parent scope is destroyed.
13657
        // The listener needs to be added after the parent is set
13658
        if (isolate || parent != this) child.$on('$destroy', destroyChild);
13659
 
13660
        return child;
13661
 
13662
        function destroyChild() {
13663
          child.$$destroyed = true;
13664
        }
13665
      },
13666
 
13667
      /**
13668
       * @ngdoc method
13669
       * @name $rootScope.Scope#$watch
13670
       * @kind function
13671
       *
13672
       * @description
13673
       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
13674
       *
13675
       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
13676
       *   $digest()} and should return the value that will be watched. (Since
13677
       *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
13678
       *   `watchExpression` can execute multiple times per
13679
       *   {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
13680
       * - The `listener` is called only when the value from the current `watchExpression` and the
13681
       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
13682
       *   see below). Inequality is determined according to reference inequality,
13683
       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
13684
       *    via the `!==` Javascript operator, unless `objectEquality == true`
13685
       *   (see next point)
13686
       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
13687
       *   according to the {@link angular.equals} function. To save the value of the object for
13688
       *   later comparison, the {@link angular.copy} function is used. This therefore means that
13689
       *   watching complex objects will have adverse memory and performance implications.
13690
       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
13691
       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
13692
       *   iteration limit is 10 to prevent an infinite loop deadlock.
13693
       *
13694
       *
13695
       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
13696
       * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
13697
       * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
13698
       * change is detected, be prepared for multiple calls to your listener.)
13699
       *
13700
       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
13701
       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
13702
       * watcher. In rare cases, this is undesirable because the listener is called when the result
13703
       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
13704
       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
13705
       * listener was called due to initialization.
13706
       *
13707
       *
13708
       *
13709
       * # Example
13710
       * ```js
13711
           // let's assume that scope was dependency injected as the $rootScope
13712
           var scope = $rootScope;
13713
           scope.name = 'misko';
13714
           scope.counter = 0;
13715
 
13716
           expect(scope.counter).toEqual(0);
13717
           scope.$watch('name', function(newValue, oldValue) {
13718
             scope.counter = scope.counter + 1;
13719
           });
13720
           expect(scope.counter).toEqual(0);
13721
 
13722
           scope.$digest();
13723
           // the listener is always called during the first $digest loop after it was registered
13724
           expect(scope.counter).toEqual(1);
13725
 
13726
           scope.$digest();
13727
           // but now it will not be called unless the value changes
13728
           expect(scope.counter).toEqual(1);
13729
 
13730
           scope.name = 'adam';
13731
           scope.$digest();
13732
           expect(scope.counter).toEqual(2);
13733
 
13734
 
13735
 
13736
           // Using a function as a watchExpression
13737
           var food;
13738
           scope.foodCounter = 0;
13739
           expect(scope.foodCounter).toEqual(0);
13740
           scope.$watch(
13741
             // This function returns the value being watched. It is called for each turn of the $digest loop
13742
             function() { return food; },
13743
             // This is the change listener, called when the value returned from the above function changes
13744
             function(newValue, oldValue) {
13745
               if ( newValue !== oldValue ) {
13746
                 // Only increment the counter if the value changed
13747
                 scope.foodCounter = scope.foodCounter + 1;
13748
               }
13749
             }
13750
           );
13751
           // No digest has been run so the counter will be zero
13752
           expect(scope.foodCounter).toEqual(0);
13753
 
13754
           // Run the digest but since food has not changed count will still be zero
13755
           scope.$digest();
13756
           expect(scope.foodCounter).toEqual(0);
13757
 
13758
           // Update food and run digest.  Now the counter will increment
13759
           food = 'cheeseburger';
13760
           scope.$digest();
13761
           expect(scope.foodCounter).toEqual(1);
13762
 
13763
       * ```
13764
       *
13765
       *
13766
       *
13767
       * @param {(function()|string)} watchExpression Expression that is evaluated on each
13768
       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
13769
       *    a call to the `listener`.
13770
       *
13771
       *    - `string`: Evaluated as {@link guide/expression expression}
13772
       *    - `function(scope)`: called with current `scope` as a parameter.
13773
       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
13774
       *    of `watchExpression` changes.
13775
       *
13776
       *    - `newVal` contains the current value of the `watchExpression`
13777
       *    - `oldVal` contains the previous value of the `watchExpression`
13778
       *    - `scope` refers to the current scope
13779
       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
13780
       *     comparing for reference equality.
13781
       * @returns {function()} Returns a deregistration function for this listener.
13782
       */
13783
      $watch: function(watchExp, listener, objectEquality) {
13784
        var get = $parse(watchExp);
13785
 
13786
        if (get.$$watchDelegate) {
13787
          return get.$$watchDelegate(this, listener, objectEquality, get);
13788
        }
13789
        var scope = this,
13790
            array = scope.$$watchers,
13791
            watcher = {
13792
              fn: listener,
13793
              last: initWatchVal,
13794
              get: get,
13795
              exp: watchExp,
13796
              eq: !!objectEquality
13797
            };
13798
 
13799
        lastDirtyWatch = null;
13800
 
13801
        if (!isFunction(listener)) {
13802
          watcher.fn = noop;
13803
        }
13804
 
13805
        if (!array) {
13806
          array = scope.$$watchers = [];
13807
        }
13808
        // we use unshift since we use a while loop in $digest for speed.
13809
        // the while loop reads in reverse order.
13810
        array.unshift(watcher);
13811
 
13812
        return function deregisterWatch() {
13813
          arrayRemove(array, watcher);
13814
          lastDirtyWatch = null;
13815
        };
13816
      },
13817
 
13818
      /**
13819
       * @ngdoc method
13820
       * @name $rootScope.Scope#$watchGroup
13821
       * @kind function
13822
       *
13823
       * @description
13824
       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
13825
       * If any one expression in the collection changes the `listener` is executed.
13826
       *
13827
       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
13828
       *   call to $digest() to see if any items changes.
13829
       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
13830
       *
13831
       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
13832
       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
13833
       *
13834
       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
13835
       *    expression in `watchExpressions` changes
13836
       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
13837
       *    those of `watchExpression`
13838
       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
13839
       *    those of `watchExpression`
13840
       *    The `scope` refers to the current scope.
13841
       * @returns {function()} Returns a de-registration function for all listeners.
13842
       */
13843
      $watchGroup: function(watchExpressions, listener) {
13844
        var oldValues = new Array(watchExpressions.length);
13845
        var newValues = new Array(watchExpressions.length);
13846
        var deregisterFns = [];
13847
        var self = this;
13848
        var changeReactionScheduled = false;
13849
        var firstRun = true;
13850
 
13851
        if (!watchExpressions.length) {
13852
          // No expressions means we call the listener ASAP
13853
          var shouldCall = true;
13854
          self.$evalAsync(function() {
13855
            if (shouldCall) listener(newValues, newValues, self);
13856
          });
13857
          return function deregisterWatchGroup() {
13858
            shouldCall = false;
13859
          };
13860
        }
13861
 
13862
        if (watchExpressions.length === 1) {
13863
          // Special case size of one
13864
          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
13865
            newValues[0] = value;
13866
            oldValues[0] = oldValue;
13867
            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
13868
          });
13869
        }
13870
 
13871
        forEach(watchExpressions, function(expr, i) {
13872
          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
13873
            newValues[i] = value;
13874
            oldValues[i] = oldValue;
13875
            if (!changeReactionScheduled) {
13876
              changeReactionScheduled = true;
13877
              self.$evalAsync(watchGroupAction);
13878
            }
13879
          });
13880
          deregisterFns.push(unwatchFn);
13881
        });
13882
 
13883
        function watchGroupAction() {
13884
          changeReactionScheduled = false;
13885
 
13886
          if (firstRun) {
13887
            firstRun = false;
13888
            listener(newValues, newValues, self);
13889
          } else {
13890
            listener(newValues, oldValues, self);
13891
          }
13892
        }
13893
 
13894
        return function deregisterWatchGroup() {
13895
          while (deregisterFns.length) {
13896
            deregisterFns.shift()();
13897
          }
13898
        };
13899
      },
13900
 
13901
 
13902
      /**
13903
       * @ngdoc method
13904
       * @name $rootScope.Scope#$watchCollection
13905
       * @kind function
13906
       *
13907
       * @description
13908
       * Shallow watches the properties of an object and fires whenever any of the properties change
13909
       * (for arrays, this implies watching the array items; for object maps, this implies watching
13910
       * the properties). If a change is detected, the `listener` callback is fired.
13911
       *
13912
       * - The `obj` collection is observed via standard $watch operation and is examined on every
13913
       *   call to $digest() to see if any items have been added, removed, or moved.
13914
       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
13915
       *   adding, removing, and moving items belonging to an object or array.
13916
       *
13917
       *
13918
       * # Example
13919
       * ```js
13920
          $scope.names = ['igor', 'matias', 'misko', 'james'];
13921
          $scope.dataCount = 4;
13922
 
13923
          $scope.$watchCollection('names', function(newNames, oldNames) {
13924
            $scope.dataCount = newNames.length;
13925
          });
13926
 
13927
          expect($scope.dataCount).toEqual(4);
13928
          $scope.$digest();
13929
 
13930
          //still at 4 ... no changes
13931
          expect($scope.dataCount).toEqual(4);
13932
 
13933
          $scope.names.pop();
13934
          $scope.$digest();
13935
 
13936
          //now there's been a change
13937
          expect($scope.dataCount).toEqual(3);
13938
       * ```
13939
       *
13940
       *
13941
       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
13942
       *    expression value should evaluate to an object or an array which is observed on each
13943
       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
13944
       *    collection will trigger a call to the `listener`.
13945
       *
13946
       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
13947
       *    when a change is detected.
13948
       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
13949
       *    - The `oldCollection` object is a copy of the former collection data.
13950
       *      Due to performance considerations, the`oldCollection` value is computed only if the
13951
       *      `listener` function declares two or more arguments.
13952
       *    - The `scope` argument refers to the current scope.
13953
       *
13954
       * @returns {function()} Returns a de-registration function for this listener. When the
13955
       *    de-registration function is executed, the internal watch operation is terminated.
13956
       */
13957
      $watchCollection: function(obj, listener) {
13958
        $watchCollectionInterceptor.$stateful = true;
13959
 
13960
        var self = this;
13961
        // the current value, updated on each dirty-check run
13962
        var newValue;
13963
        // a shallow copy of the newValue from the last dirty-check run,
13964
        // updated to match newValue during dirty-check run
13965
        var oldValue;
13966
        // a shallow copy of the newValue from when the last change happened
13967
        var veryOldValue;
13968
        // only track veryOldValue if the listener is asking for it
13969
        var trackVeryOldValue = (listener.length > 1);
13970
        var changeDetected = 0;
13971
        var changeDetector = $parse(obj, $watchCollectionInterceptor);
13972
        var internalArray = [];
13973
        var internalObject = {};
13974
        var initRun = true;
13975
        var oldLength = 0;
13976
 
13977
        function $watchCollectionInterceptor(_value) {
13978
          newValue = _value;
13979
          var newLength, key, bothNaN, newItem, oldItem;
13980
 
13981
          // If the new value is undefined, then return undefined as the watch may be a one-time watch
13982
          if (isUndefined(newValue)) return;
13983
 
13984
          if (!isObject(newValue)) { // if primitive
13985
            if (oldValue !== newValue) {
13986
              oldValue = newValue;
13987
              changeDetected++;
13988
            }
13989
          } else if (isArrayLike(newValue)) {
13990
            if (oldValue !== internalArray) {
13991
              // we are transitioning from something which was not an array into array.
13992
              oldValue = internalArray;
13993
              oldLength = oldValue.length = 0;
13994
              changeDetected++;
13995
            }
13996
 
13997
            newLength = newValue.length;
13998
 
13999
            if (oldLength !== newLength) {
14000
              // if lengths do not match we need to trigger change notification
14001
              changeDetected++;
14002
              oldValue.length = oldLength = newLength;
14003
            }
14004
            // copy the items to oldValue and look for changes.
14005
            for (var i = 0; i < newLength; i++) {
14006
              oldItem = oldValue[i];
14007
              newItem = newValue[i];
14008
 
14009
              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14010
              if (!bothNaN && (oldItem !== newItem)) {
14011
                changeDetected++;
14012
                oldValue[i] = newItem;
14013
              }
14014
            }
14015
          } else {
14016
            if (oldValue !== internalObject) {
14017
              // we are transitioning from something which was not an object into object.
14018
              oldValue = internalObject = {};
14019
              oldLength = 0;
14020
              changeDetected++;
14021
            }
14022
            // copy the items to oldValue and look for changes.
14023
            newLength = 0;
14024
            for (key in newValue) {
14025
              if (newValue.hasOwnProperty(key)) {
14026
                newLength++;
14027
                newItem = newValue[key];
14028
                oldItem = oldValue[key];
14029
 
14030
                if (key in oldValue) {
14031
                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14032
                  if (!bothNaN && (oldItem !== newItem)) {
14033
                    changeDetected++;
14034
                    oldValue[key] = newItem;
14035
                  }
14036
                } else {
14037
                  oldLength++;
14038
                  oldValue[key] = newItem;
14039
                  changeDetected++;
14040
                }
14041
              }
14042
            }
14043
            if (oldLength > newLength) {
14044
              // we used to have more keys, need to find them and destroy them.
14045
              changeDetected++;
14046
              for (key in oldValue) {
14047
                if (!newValue.hasOwnProperty(key)) {
14048
                  oldLength--;
14049
                  delete oldValue[key];
14050
                }
14051
              }
14052
            }
14053
          }
14054
          return changeDetected;
14055
        }
14056
 
14057
        function $watchCollectionAction() {
14058
          if (initRun) {
14059
            initRun = false;
14060
            listener(newValue, newValue, self);
14061
          } else {
14062
            listener(newValue, veryOldValue, self);
14063
          }
14064
 
14065
          // make a copy for the next time a collection is changed
14066
          if (trackVeryOldValue) {
14067
            if (!isObject(newValue)) {
14068
              //primitive
14069
              veryOldValue = newValue;
14070
            } else if (isArrayLike(newValue)) {
14071
              veryOldValue = new Array(newValue.length);
14072
              for (var i = 0; i < newValue.length; i++) {
14073
                veryOldValue[i] = newValue[i];
14074
              }
14075
            } else { // if object
14076
              veryOldValue = {};
14077
              for (var key in newValue) {
14078
                if (hasOwnProperty.call(newValue, key)) {
14079
                  veryOldValue[key] = newValue[key];
14080
                }
14081
              }
14082
            }
14083
          }
14084
        }
14085
 
14086
        return this.$watch(changeDetector, $watchCollectionAction);
14087
      },
14088
 
14089
      /**
14090
       * @ngdoc method
14091
       * @name $rootScope.Scope#$digest
14092
       * @kind function
14093
       *
14094
       * @description
14095
       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
14096
       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
14097
       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
14098
       * until no more listeners are firing. This means that it is possible to get into an infinite
14099
       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
14100
       * iterations exceeds 10.
14101
       *
14102
       * Usually, you don't call `$digest()` directly in
14103
       * {@link ng.directive:ngController controllers} or in
14104
       * {@link ng.$compileProvider#directive directives}.
14105
       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
14106
       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
14107
       *
14108
       * If you want to be notified whenever `$digest()` is called,
14109
       * you can register a `watchExpression` function with
14110
       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
14111
       *
14112
       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
14113
       *
14114
       * # Example
14115
       * ```js
14116
           var scope = ...;
14117
           scope.name = 'misko';
14118
           scope.counter = 0;
14119
 
14120
           expect(scope.counter).toEqual(0);
14121
           scope.$watch('name', function(newValue, oldValue) {
14122
             scope.counter = scope.counter + 1;
14123
           });
14124
           expect(scope.counter).toEqual(0);
14125
 
14126
           scope.$digest();
14127
           // the listener is always called during the first $digest loop after it was registered
14128
           expect(scope.counter).toEqual(1);
14129
 
14130
           scope.$digest();
14131
           // but now it will not be called unless the value changes
14132
           expect(scope.counter).toEqual(1);
14133
 
14134
           scope.name = 'adam';
14135
           scope.$digest();
14136
           expect(scope.counter).toEqual(2);
14137
       * ```
14138
       *
14139
       */
14140
      $digest: function() {
14141
        var watch, value, last,
14142
            watchers,
14143
            length,
14144
            dirty, ttl = TTL,
14145
            next, current, target = this,
14146
            watchLog = [],
14147
            logIdx, logMsg, asyncTask;
14148
 
14149
        beginPhase('$digest');
14150
        // Check for changes to browser url that happened in sync before the call to $digest
14151
        $browser.$$checkUrlChange();
14152
 
14153
        if (this === $rootScope && applyAsyncId !== null) {
14154
          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
14155
          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
14156
          $browser.defer.cancel(applyAsyncId);
14157
          flushApplyAsync();
14158
        }
14159
 
14160
        lastDirtyWatch = null;
14161
 
14162
        do { // "while dirty" loop
14163
          dirty = false;
14164
          current = target;
14165
 
14166
          while (asyncQueue.length) {
14167
            try {
14168
              asyncTask = asyncQueue.shift();
14169
              asyncTask.scope.$eval(asyncTask.expression);
14170
            } catch (e) {
14171
              $exceptionHandler(e);
14172
            }
14173
            lastDirtyWatch = null;
14174
          }
14175
 
14176
          traverseScopesLoop:
14177
          do { // "traverse the scopes" loop
14178
            if ((watchers = current.$$watchers)) {
14179
              // process our watches
14180
              length = watchers.length;
14181
              while (length--) {
14182
                try {
14183
                  watch = watchers[length];
14184
                  // Most common watches are on primitives, in which case we can short
14185
                  // circuit it with === operator, only when === fails do we use .equals
14186
                  if (watch) {
14187
                    if ((value = watch.get(current)) !== (last = watch.last) &&
14188
                        !(watch.eq
14189
                            ? equals(value, last)
14190
                            : (typeof value === 'number' && typeof last === 'number'
14191
                               && isNaN(value) && isNaN(last)))) {
14192
                      dirty = true;
14193
                      lastDirtyWatch = watch;
14194
                      watch.last = watch.eq ? copy(value, null) : value;
14195
                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
14196
                      if (ttl < 5) {
14197
                        logIdx = 4 - ttl;
14198
                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
14199
                        watchLog[logIdx].push({
14200
                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14201
                          newVal: value,
14202
                          oldVal: last
14203
                        });
14204
                      }
14205
                    } else if (watch === lastDirtyWatch) {
14206
                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
14207
                      // have already been tested.
14208
                      dirty = false;
14209
                      break traverseScopesLoop;
14210
                    }
14211
                  }
14212
                } catch (e) {
14213
                  $exceptionHandler(e);
14214
                }
14215
              }
14216
            }
14217
 
14218
            // Insanity Warning: scope depth-first traversal
14219
            // yes, this code is a bit crazy, but it works and we have tests to prove it!
14220
            // this piece should be kept in sync with the traversal in $broadcast
14221
            if (!(next = (current.$$childHead ||
14222
                (current !== target && current.$$nextSibling)))) {
14223
              while (current !== target && !(next = current.$$nextSibling)) {
14224
                current = current.$parent;
14225
              }
14226
            }
14227
          } while ((current = next));
14228
 
14229
          // `break traverseScopesLoop;` takes us to here
14230
 
14231
          if ((dirty || asyncQueue.length) && !(ttl--)) {
14232
            clearPhase();
14233
            throw $rootScopeMinErr('infdig',
14234
                '{0} $digest() iterations reached. Aborting!\n' +
14235
                'Watchers fired in the last 5 iterations: {1}',
14236
                TTL, watchLog);
14237
          }
14238
 
14239
        } while (dirty || asyncQueue.length);
14240
 
14241
        clearPhase();
14242
 
14243
        while (postDigestQueue.length) {
14244
          try {
14245
            postDigestQueue.shift()();
14246
          } catch (e) {
14247
            $exceptionHandler(e);
14248
          }
14249
        }
14250
      },
14251
 
14252
 
14253
      /**
14254
       * @ngdoc event
14255
       * @name $rootScope.Scope#$destroy
14256
       * @eventType broadcast on scope being destroyed
14257
       *
14258
       * @description
14259
       * Broadcasted when a scope and its children are being destroyed.
14260
       *
14261
       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14262
       * clean up DOM bindings before an element is removed from the DOM.
14263
       */
14264
 
14265
      /**
14266
       * @ngdoc method
14267
       * @name $rootScope.Scope#$destroy
14268
       * @kind function
14269
       *
14270
       * @description
14271
       * Removes the current scope (and all of its children) from the parent scope. Removal implies
14272
       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
14273
       * propagate to the current scope and its children. Removal also implies that the current
14274
       * scope is eligible for garbage collection.
14275
       *
14276
       * The `$destroy()` is usually used by directives such as
14277
       * {@link ng.directive:ngRepeat ngRepeat} for managing the
14278
       * unrolling of the loop.
14279
       *
14280
       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
14281
       * Application code can register a `$destroy` event handler that will give it a chance to
14282
       * perform any necessary cleanup.
14283
       *
14284
       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14285
       * clean up DOM bindings before an element is removed from the DOM.
14286
       */
14287
      $destroy: function() {
14288
        // we can't destroy the root scope or a scope that has been already destroyed
14289
        if (this.$$destroyed) return;
14290
        var parent = this.$parent;
14291
 
14292
        this.$broadcast('$destroy');
14293
        this.$$destroyed = true;
14294
        if (this === $rootScope) return;
14295
 
14296
        for (var eventName in this.$$listenerCount) {
14297
          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
14298
        }
14299
 
14300
        // sever all the references to parent scopes (after this cleanup, the current scope should
14301
        // not be retained by any of our references and should be eligible for garbage collection)
14302
        if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
14303
        if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
14304
        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
14305
        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
14306
 
14307
        // Disable listeners, watchers and apply/digest methods
14308
        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
14309
        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
14310
        this.$$listeners = {};
14311
 
14312
        // All of the code below is bogus code that works around V8's memory leak via optimized code
14313
        // and inline caches.
14314
        //
14315
        // see:
14316
        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
14317
        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
14318
        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
14319
 
14320
        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
14321
            this.$$childTail = this.$root = this.$$watchers = null;
14322
      },
14323
 
14324
      /**
14325
       * @ngdoc method
14326
       * @name $rootScope.Scope#$eval
14327
       * @kind function
14328
       *
14329
       * @description
14330
       * Executes the `expression` on the current scope and returns the result. Any exceptions in
14331
       * the expression are propagated (uncaught). This is useful when evaluating Angular
14332
       * expressions.
14333
       *
14334
       * # Example
14335
       * ```js
14336
           var scope = ng.$rootScope.Scope();
14337
           scope.a = 1;
14338
           scope.b = 2;
14339
 
14340
           expect(scope.$eval('a+b')).toEqual(3);
14341
           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
14342
       * ```
14343
       *
14344
       * @param {(string|function())=} expression An angular expression to be executed.
14345
       *
14346
       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
14347
       *    - `function(scope)`: execute the function with the current `scope` parameter.
14348
       *
14349
       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14350
       * @returns {*} The result of evaluating the expression.
14351
       */
14352
      $eval: function(expr, locals) {
14353
        return $parse(expr)(this, locals);
14354
      },
14355
 
14356
      /**
14357
       * @ngdoc method
14358
       * @name $rootScope.Scope#$evalAsync
14359
       * @kind function
14360
       *
14361
       * @description
14362
       * Executes the expression on the current scope at a later point in time.
14363
       *
14364
       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
14365
       * that:
14366
       *
14367
       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
14368
       *     rendering).
14369
       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
14370
       *     `expression` execution.
14371
       *
14372
       * Any exceptions from the execution of the expression are forwarded to the
14373
       * {@link ng.$exceptionHandler $exceptionHandler} service.
14374
       *
14375
       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
14376
       * will be scheduled. However, it is encouraged to always call code that changes the model
14377
       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
14378
       *
14379
       * @param {(string|function())=} expression An angular expression to be executed.
14380
       *
14381
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14382
       *    - `function(scope)`: execute the function with the current `scope` parameter.
14383
       *
14384
       */
14385
      $evalAsync: function(expr) {
14386
        // if we are outside of an $digest loop and this is the first time we are scheduling async
14387
        // task also schedule async auto-flush
14388
        if (!$rootScope.$$phase && !asyncQueue.length) {
14389
          $browser.defer(function() {
14390
            if (asyncQueue.length) {
14391
              $rootScope.$digest();
14392
            }
14393
          });
14394
        }
14395
 
14396
        asyncQueue.push({scope: this, expression: expr});
14397
      },
14398
 
14399
      $$postDigest: function(fn) {
14400
        postDigestQueue.push(fn);
14401
      },
14402
 
14403
      /**
14404
       * @ngdoc method
14405
       * @name $rootScope.Scope#$apply
14406
       * @kind function
14407
       *
14408
       * @description
14409
       * `$apply()` is used to execute an expression in angular from outside of the angular
14410
       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
14411
       * Because we are calling into the angular framework we need to perform proper scope life
14412
       * cycle of {@link ng.$exceptionHandler exception handling},
14413
       * {@link ng.$rootScope.Scope#$digest executing watches}.
14414
       *
14415
       * ## Life cycle
14416
       *
14417
       * # Pseudo-Code of `$apply()`
14418
       * ```js
14419
           function $apply(expr) {
14420
             try {
14421
               return $eval(expr);
14422
             } catch (e) {
14423
               $exceptionHandler(e);
14424
             } finally {
14425
               $root.$digest();
14426
             }
14427
           }
14428
       * ```
14429
       *
14430
       *
14431
       * Scope's `$apply()` method transitions through the following stages:
14432
       *
14433
       * 1. The {@link guide/expression expression} is executed using the
14434
       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
14435
       * 2. Any exceptions from the execution of the expression are forwarded to the
14436
       *    {@link ng.$exceptionHandler $exceptionHandler} service.
14437
       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
14438
       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
14439
       *
14440
       *
14441
       * @param {(string|function())=} exp An angular expression to be executed.
14442
       *
14443
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14444
       *    - `function(scope)`: execute the function with current `scope` parameter.
14445
       *
14446
       * @returns {*} The result of evaluating the expression.
14447
       */
14448
      $apply: function(expr) {
14449
        try {
14450
          beginPhase('$apply');
14451
          return this.$eval(expr);
14452
        } catch (e) {
14453
          $exceptionHandler(e);
14454
        } finally {
14455
          clearPhase();
14456
          try {
14457
            $rootScope.$digest();
14458
          } catch (e) {
14459
            $exceptionHandler(e);
14460
            throw e;
14461
          }
14462
        }
14463
      },
14464
 
14465
      /**
14466
       * @ngdoc method
14467
       * @name $rootScope.Scope#$applyAsync
14468
       * @kind function
14469
       *
14470
       * @description
14471
       * Schedule the invokation of $apply to occur at a later time. The actual time difference
14472
       * varies across browsers, but is typically around ~10 milliseconds.
14473
       *
14474
       * This can be used to queue up multiple expressions which need to be evaluated in the same
14475
       * digest.
14476
       *
14477
       * @param {(string|function())=} exp An angular expression to be executed.
14478
       *
14479
       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
14480
       *    - `function(scope)`: execute the function with current `scope` parameter.
14481
       */
14482
      $applyAsync: function(expr) {
14483
        var scope = this;
14484
        expr && applyAsyncQueue.push($applyAsyncExpression);
14485
        scheduleApplyAsync();
14486
 
14487
        function $applyAsyncExpression() {
14488
          scope.$eval(expr);
14489
        }
14490
      },
14491
 
14492
      /**
14493
       * @ngdoc method
14494
       * @name $rootScope.Scope#$on
14495
       * @kind function
14496
       *
14497
       * @description
14498
       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
14499
       * discussion of event life cycle.
14500
       *
14501
       * The event listener function format is: `function(event, args...)`. The `event` object
14502
       * passed into the listener has the following attributes:
14503
       *
14504
       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
14505
       *     `$broadcast`-ed.
14506
       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
14507
       *     event propagates through the scope hierarchy, this property is set to null.
14508
       *   - `name` - `{string}`: name of the event.
14509
       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
14510
       *     further event propagation (available only for events that were `$emit`-ed).
14511
       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
14512
       *     to true.
14513
       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
14514
       *
14515
       * @param {string} name Event name to listen on.
14516
       * @param {function(event, ...args)} listener Function to call when the event is emitted.
14517
       * @returns {function()} Returns a deregistration function for this listener.
14518
       */
14519
      $on: function(name, listener) {
14520
        var namedListeners = this.$$listeners[name];
14521
        if (!namedListeners) {
14522
          this.$$listeners[name] = namedListeners = [];
14523
        }
14524
        namedListeners.push(listener);
14525
 
14526
        var current = this;
14527
        do {
14528
          if (!current.$$listenerCount[name]) {
14529
            current.$$listenerCount[name] = 0;
14530
          }
14531
          current.$$listenerCount[name]++;
14532
        } while ((current = current.$parent));
14533
 
14534
        var self = this;
14535
        return function() {
14536
          var indexOfListener = namedListeners.indexOf(listener);
14537
          if (indexOfListener !== -1) {
14538
            namedListeners[indexOfListener] = null;
14539
            decrementListenerCount(self, 1, name);
14540
          }
14541
        };
14542
      },
14543
 
14544
 
14545
      /**
14546
       * @ngdoc method
14547
       * @name $rootScope.Scope#$emit
14548
       * @kind function
14549
       *
14550
       * @description
14551
       * Dispatches an event `name` upwards through the scope hierarchy notifying the
14552
       * registered {@link ng.$rootScope.Scope#$on} listeners.
14553
       *
14554
       * The event life cycle starts at the scope on which `$emit` was called. All
14555
       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14556
       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
14557
       * registered listeners along the way. The event will stop propagating if one of the listeners
14558
       * cancels it.
14559
       *
14560
       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14561
       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14562
       *
14563
       * @param {string} name Event name to emit.
14564
       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14565
       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
14566
       */
14567
      $emit: function(name, args) {
14568
        var empty = [],
14569
            namedListeners,
14570
            scope = this,
14571
            stopPropagation = false,
14572
            event = {
14573
              name: name,
14574
              targetScope: scope,
14575
              stopPropagation: function() {stopPropagation = true;},
14576
              preventDefault: function() {
14577
                event.defaultPrevented = true;
14578
              },
14579
              defaultPrevented: false
14580
            },
14581
            listenerArgs = concat([event], arguments, 1),
14582
            i, length;
14583
 
14584
        do {
14585
          namedListeners = scope.$$listeners[name] || empty;
14586
          event.currentScope = scope;
14587
          for (i = 0, length = namedListeners.length; i < length; i++) {
14588
 
14589
            // if listeners were deregistered, defragment the array
14590
            if (!namedListeners[i]) {
14591
              namedListeners.splice(i, 1);
14592
              i--;
14593
              length--;
14594
              continue;
14595
            }
14596
            try {
14597
              //allow all listeners attached to the current scope to run
14598
              namedListeners[i].apply(null, listenerArgs);
14599
            } catch (e) {
14600
              $exceptionHandler(e);
14601
            }
14602
          }
14603
          //if any listener on the current scope stops propagation, prevent bubbling
14604
          if (stopPropagation) {
14605
            event.currentScope = null;
14606
            return event;
14607
          }
14608
          //traverse upwards
14609
          scope = scope.$parent;
14610
        } while (scope);
14611
 
14612
        event.currentScope = null;
14613
 
14614
        return event;
14615
      },
14616
 
14617
 
14618
      /**
14619
       * @ngdoc method
14620
       * @name $rootScope.Scope#$broadcast
14621
       * @kind function
14622
       *
14623
       * @description
14624
       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
14625
       * registered {@link ng.$rootScope.Scope#$on} listeners.
14626
       *
14627
       * The event life cycle starts at the scope on which `$broadcast` was called. All
14628
       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14629
       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
14630
       * scope and calls all registered listeners along the way. The event cannot be canceled.
14631
       *
14632
       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14633
       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14634
       *
14635
       * @param {string} name Event name to broadcast.
14636
       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14637
       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
14638
       */
14639
      $broadcast: function(name, args) {
14640
        var target = this,
14641
            current = target,
14642
            next = target,
14643
            event = {
14644
              name: name,
14645
              targetScope: target,
14646
              preventDefault: function() {
14647
                event.defaultPrevented = true;
14648
              },
14649
              defaultPrevented: false
14650
            };
14651
 
14652
        if (!target.$$listenerCount[name]) return event;
14653
 
14654
        var listenerArgs = concat([event], arguments, 1),
14655
            listeners, i, length;
14656
 
14657
        //down while you can, then up and next sibling or up and next sibling until back at root
14658
        while ((current = next)) {
14659
          event.currentScope = current;
14660
          listeners = current.$$listeners[name] || [];
14661
          for (i = 0, length = listeners.length; i < length; i++) {
14662
            // if listeners were deregistered, defragment the array
14663
            if (!listeners[i]) {
14664
              listeners.splice(i, 1);
14665
              i--;
14666
              length--;
14667
              continue;
14668
            }
14669
 
14670
            try {
14671
              listeners[i].apply(null, listenerArgs);
14672
            } catch (e) {
14673
              $exceptionHandler(e);
14674
            }
14675
          }
14676
 
14677
          // Insanity Warning: scope depth-first traversal
14678
          // yes, this code is a bit crazy, but it works and we have tests to prove it!
14679
          // this piece should be kept in sync with the traversal in $digest
14680
          // (though it differs due to having the extra check for $$listenerCount)
14681
          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
14682
              (current !== target && current.$$nextSibling)))) {
14683
            while (current !== target && !(next = current.$$nextSibling)) {
14684
              current = current.$parent;
14685
            }
14686
          }
14687
        }
14688
 
14689
        event.currentScope = null;
14690
        return event;
14691
      }
14692
    };
14693
 
14694
    var $rootScope = new Scope();
14695
 
14696
    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
14697
    var asyncQueue = $rootScope.$$asyncQueue = [];
14698
    var postDigestQueue = $rootScope.$$postDigestQueue = [];
14699
    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
14700
 
14701
    return $rootScope;
14702
 
14703
 
14704
    function beginPhase(phase) {
14705
      if ($rootScope.$$phase) {
14706
        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
14707
      }
14708
 
14709
      $rootScope.$$phase = phase;
14710
    }
14711
 
14712
    function clearPhase() {
14713
      $rootScope.$$phase = null;
14714
    }
14715
 
14716
 
14717
    function decrementListenerCount(current, count, name) {
14718
      do {
14719
        current.$$listenerCount[name] -= count;
14720
 
14721
        if (current.$$listenerCount[name] === 0) {
14722
          delete current.$$listenerCount[name];
14723
        }
14724
      } while ((current = current.$parent));
14725
    }
14726
 
14727
    /**
14728
     * function used as an initial value for watchers.
14729
     * because it's unique we can easily tell it apart from other values
14730
     */
14731
    function initWatchVal() {}
14732
 
14733
    function flushApplyAsync() {
14734
      while (applyAsyncQueue.length) {
14735
        try {
14736
          applyAsyncQueue.shift()();
14737
        } catch (e) {
14738
          $exceptionHandler(e);
14739
        }
14740
      }
14741
      applyAsyncId = null;
14742
    }
14743
 
14744
    function scheduleApplyAsync() {
14745
      if (applyAsyncId === null) {
14746
        applyAsyncId = $browser.defer(function() {
14747
          $rootScope.$apply(flushApplyAsync);
14748
        });
14749
      }
14750
    }
14751
  }];
14752
}
14753
 
14754
/**
14755
 * @description
14756
 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
14757
 */
14758
function $$SanitizeUriProvider() {
14759
  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14760
    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
14761
 
14762
  /**
14763
   * @description
14764
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14765
   * urls during a[href] sanitization.
14766
   *
14767
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14768
   *
14769
   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
14770
   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
14771
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14772
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14773
   *
14774
   * @param {RegExp=} regexp New regexp to whitelist urls with.
14775
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14776
   *    chaining otherwise.
14777
   */
14778
  this.aHrefSanitizationWhitelist = function(regexp) {
14779
    if (isDefined(regexp)) {
14780
      aHrefSanitizationWhitelist = regexp;
14781
      return this;
14782
    }
14783
    return aHrefSanitizationWhitelist;
14784
  };
14785
 
14786
 
14787
  /**
14788
   * @description
14789
   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14790
   * urls during img[src] sanitization.
14791
   *
14792
   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14793
   *
14794
   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
14795
   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
14796
   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14797
   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14798
   *
14799
   * @param {RegExp=} regexp New regexp to whitelist urls with.
14800
   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14801
   *    chaining otherwise.
14802
   */
14803
  this.imgSrcSanitizationWhitelist = function(regexp) {
14804
    if (isDefined(regexp)) {
14805
      imgSrcSanitizationWhitelist = regexp;
14806
      return this;
14807
    }
14808
    return imgSrcSanitizationWhitelist;
14809
  };
14810
 
14811
  this.$get = function() {
14812
    return function sanitizeUri(uri, isImage) {
14813
      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
14814
      var normalizedVal;
14815
      normalizedVal = urlResolve(uri).href;
14816
      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
14817
        return 'unsafe:' + normalizedVal;
14818
      }
14819
      return uri;
14820
    };
14821
  };
14822
}
14823
 
14824
var $sceMinErr = minErr('$sce');
14825
 
14826
var SCE_CONTEXTS = {
14827
  HTML: 'html',
14828
  CSS: 'css',
14829
  URL: 'url',
14830
  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
14831
  // url.  (e.g. ng-include, script src, templateUrl)
14832
  RESOURCE_URL: 'resourceUrl',
14833
  JS: 'js'
14834
};
14835
 
14836
// Helper functions follow.
14837
 
14838
function adjustMatcher(matcher) {
14839
  if (matcher === 'self') {
14840
    return matcher;
14841
  } else if (isString(matcher)) {
14842
    // Strings match exactly except for 2 wildcards - '*' and '**'.
14843
    // '*' matches any character except those from the set ':/.?&'.
14844
    // '**' matches any character (like .* in a RegExp).
14845
    // More than 2 *'s raises an error as it's ill defined.
14846
    if (matcher.indexOf('***') > -1) {
14847
      throw $sceMinErr('iwcard',
14848
          'Illegal sequence *** in string matcher.  String: {0}', matcher);
14849
    }
14850
    matcher = escapeForRegexp(matcher).
14851
                  replace('\\*\\*', '.*').
14852
                  replace('\\*', '[^:/.?&;]*');
14853
    return new RegExp('^' + matcher + '$');
14854
  } else if (isRegExp(matcher)) {
14855
    // The only other type of matcher allowed is a Regexp.
14856
    // Match entire URL / disallow partial matches.
14857
    // Flags are reset (i.e. no global, ignoreCase or multiline)
14858
    return new RegExp('^' + matcher.source + '$');
14859
  } else {
14860
    throw $sceMinErr('imatcher',
14861
        'Matchers may only be "self", string patterns or RegExp objects');
14862
  }
14863
}
14864
 
14865
 
14866
function adjustMatchers(matchers) {
14867
  var adjustedMatchers = [];
14868
  if (isDefined(matchers)) {
14869
    forEach(matchers, function(matcher) {
14870
      adjustedMatchers.push(adjustMatcher(matcher));
14871
    });
14872
  }
14873
  return adjustedMatchers;
14874
}
14875
 
14876
 
14877
/**
14878
 * @ngdoc service
14879
 * @name $sceDelegate
14880
 * @kind function
14881
 *
14882
 * @description
14883
 *
14884
 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
14885
 * Contextual Escaping (SCE)} services to AngularJS.
14886
 *
14887
 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
14888
 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
14889
 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
14890
 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
14891
 * work because `$sce` delegates to `$sceDelegate` for these operations.
14892
 *
14893
 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
14894
 *
14895
 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
14896
 * can override it completely to change the behavior of `$sce`, the common case would
14897
 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
14898
 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
14899
 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
14900
 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
14901
 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14902
 */
14903
 
14904
/**
14905
 * @ngdoc provider
14906
 * @name $sceDelegateProvider
14907
 * @description
14908
 *
14909
 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
14910
 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
14911
 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
14912
 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
14913
 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14914
 *
14915
 * For the general details about this service in Angular, read the main page for {@link ng.$sce
14916
 * Strict Contextual Escaping (SCE)}.
14917
 *
14918
 * **Example**:  Consider the following case. <a name="example"></a>
14919
 *
14920
 * - your app is hosted at url `http://myapp.example.com/`
14921
 * - but some of your templates are hosted on other domains you control such as
14922
 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
14923
 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
14924
 *
14925
 * Here is what a secure configuration for this scenario might look like:
14926
 *
14927
 * ```
14928
 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
14929
 *    $sceDelegateProvider.resourceUrlWhitelist([
14930
 *      // Allow same origin resource loads.
14931
 *      'self',
14932
 *      // Allow loading from our assets domain.  Notice the difference between * and **.
14933
 *      'http://srv*.assets.example.com/**'
14934
 *    ]);
14935
 *
14936
 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
14937
 *    $sceDelegateProvider.resourceUrlBlacklist([
14938
 *      'http://myapp.example.com/clickThru**'
14939
 *    ]);
14940
 *  });
14941
 * ```
14942
 */
14943
 
14944
function $SceDelegateProvider() {
14945
  this.SCE_CONTEXTS = SCE_CONTEXTS;
14946
 
14947
  // Resource URLs can also be trusted by policy.
14948
  var resourceUrlWhitelist = ['self'],
14949
      resourceUrlBlacklist = [];
14950
 
14951
  /**
14952
   * @ngdoc method
14953
   * @name $sceDelegateProvider#resourceUrlWhitelist
14954
   * @kind function
14955
   *
14956
   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
14957
   *     provided.  This must be an array or null.  A snapshot of this array is used so further
14958
   *     changes to the array are ignored.
14959
   *
14960
   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
14961
   *     allowed in this array.
14962
   *
14963
   *     Note: **an empty whitelist array will block all URLs**!
14964
   *
14965
   * @return {Array} the currently set whitelist array.
14966
   *
14967
   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
14968
   * same origin resource requests.
14969
   *
14970
   * @description
14971
   * Sets/Gets the whitelist of trusted resource URLs.
14972
   */
14973
  this.resourceUrlWhitelist = function(value) {
14974
    if (arguments.length) {
14975
      resourceUrlWhitelist = adjustMatchers(value);
14976
    }
14977
    return resourceUrlWhitelist;
14978
  };
14979
 
14980
  /**
14981
   * @ngdoc method
14982
   * @name $sceDelegateProvider#resourceUrlBlacklist
14983
   * @kind function
14984
   *
14985
   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
14986
   *     provided.  This must be an array or null.  A snapshot of this array is used so further
14987
   *     changes to the array are ignored.
14988
   *
14989
   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
14990
   *     allowed in this array.
14991
   *
14992
   *     The typical usage for the blacklist is to **block
14993
   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
14994
   *     these would otherwise be trusted but actually return content from the redirected domain.
14995
   *
14996
   *     Finally, **the blacklist overrides the whitelist** and has the final say.
14997
   *
14998
   * @return {Array} the currently set blacklist array.
14999
   *
15000
   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
15001
   * is no blacklist.)
15002
   *
15003
   * @description
15004
   * Sets/Gets the blacklist of trusted resource URLs.
15005
   */
15006
 
15007
  this.resourceUrlBlacklist = function(value) {
15008
    if (arguments.length) {
15009
      resourceUrlBlacklist = adjustMatchers(value);
15010
    }
15011
    return resourceUrlBlacklist;
15012
  };
15013
 
15014
  this.$get = ['$injector', function($injector) {
15015
 
15016
    var htmlSanitizer = function htmlSanitizer(html) {
15017
      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15018
    };
15019
 
15020
    if ($injector.has('$sanitize')) {
15021
      htmlSanitizer = $injector.get('$sanitize');
15022
    }
15023
 
15024
 
15025
    function matchUrl(matcher, parsedUrl) {
15026
      if (matcher === 'self') {
15027
        return urlIsSameOrigin(parsedUrl);
15028
      } else {
15029
        // definitely a regex.  See adjustMatchers()
15030
        return !!matcher.exec(parsedUrl.href);
15031
      }
15032
    }
15033
 
15034
    function isResourceUrlAllowedByPolicy(url) {
15035
      var parsedUrl = urlResolve(url.toString());
15036
      var i, n, allowed = false;
15037
      // Ensure that at least one item from the whitelist allows this url.
15038
      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
15039
        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
15040
          allowed = true;
15041
          break;
15042
        }
15043
      }
15044
      if (allowed) {
15045
        // Ensure that no item from the blacklist blocked this url.
15046
        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
15047
          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
15048
            allowed = false;
15049
            break;
15050
          }
15051
        }
15052
      }
15053
      return allowed;
15054
    }
15055
 
15056
    function generateHolderType(Base) {
15057
      var holderType = function TrustedValueHolderType(trustedValue) {
15058
        this.$$unwrapTrustedValue = function() {
15059
          return trustedValue;
15060
        };
15061
      };
15062
      if (Base) {
15063
        holderType.prototype = new Base();
15064
      }
15065
      holderType.prototype.valueOf = function sceValueOf() {
15066
        return this.$$unwrapTrustedValue();
15067
      };
15068
      holderType.prototype.toString = function sceToString() {
15069
        return this.$$unwrapTrustedValue().toString();
15070
      };
15071
      return holderType;
15072
    }
15073
 
15074
    var trustedValueHolderBase = generateHolderType(),
15075
        byType = {};
15076
 
15077
    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
15078
    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
15079
    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
15080
    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
15081
    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
15082
 
15083
    /**
15084
     * @ngdoc method
15085
     * @name $sceDelegate#trustAs
15086
     *
15087
     * @description
15088
     * Returns an object that is trusted by angular for use in specified strict
15089
     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
15090
     * attribute interpolation, any dom event binding attribute interpolation
15091
     * such as for onclick,  etc.) that uses the provided value.
15092
     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
15093
     *
15094
     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
15095
     *   resourceUrl, html, js and css.
15096
     * @param {*} value The value that that should be considered trusted/safe.
15097
     * @returns {*} A value that can be used to stand in for the provided `value` in places
15098
     * where Angular expects a $sce.trustAs() return value.
15099
     */
15100
    function trustAs(type, trustedValue) {
15101
      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15102
      if (!Constructor) {
15103
        throw $sceMinErr('icontext',
15104
            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
15105
            type, trustedValue);
15106
      }
15107
      if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
15108
        return trustedValue;
15109
      }
15110
      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
15111
      // mutable objects, we ensure here that the value passed in is actually a string.
15112
      if (typeof trustedValue !== 'string') {
15113
        throw $sceMinErr('itype',
15114
            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
15115
            type);
15116
      }
15117
      return new Constructor(trustedValue);
15118
    }
15119
 
15120
    /**
15121
     * @ngdoc method
15122
     * @name $sceDelegate#valueOf
15123
     *
15124
     * @description
15125
     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
15126
     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
15127
     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
15128
     *
15129
     * If the passed parameter is not a value that had been returned by {@link
15130
     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
15131
     *
15132
     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
15133
     *      call or anything else.
15134
     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
15135
     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
15136
     *     `value` unchanged.
15137
     */
15138
    function valueOf(maybeTrusted) {
15139
      if (maybeTrusted instanceof trustedValueHolderBase) {
15140
        return maybeTrusted.$$unwrapTrustedValue();
15141
      } else {
15142
        return maybeTrusted;
15143
      }
15144
    }
15145
 
15146
    /**
15147
     * @ngdoc method
15148
     * @name $sceDelegate#getTrusted
15149
     *
15150
     * @description
15151
     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
15152
     * returns the originally supplied value if the queried context type is a supertype of the
15153
     * created type.  If this condition isn't satisfied, throws an exception.
15154
     *
15155
     * @param {string} type The kind of context in which this value is to be used.
15156
     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
15157
     *     `$sceDelegate.trustAs`} call.
15158
     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
15159
     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
15160
     */
15161
    function getTrusted(type, maybeTrusted) {
15162
      if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
15163
        return maybeTrusted;
15164
      }
15165
      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15166
      if (constructor && maybeTrusted instanceof constructor) {
15167
        return maybeTrusted.$$unwrapTrustedValue();
15168
      }
15169
      // If we get here, then we may only take one of two actions.
15170
      // 1. sanitize the value for the requested type, or
15171
      // 2. throw an exception.
15172
      if (type === SCE_CONTEXTS.RESOURCE_URL) {
15173
        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
15174
          return maybeTrusted;
15175
        } else {
15176
          throw $sceMinErr('insecurl',
15177
              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
15178
              maybeTrusted.toString());
15179
        }
15180
      } else if (type === SCE_CONTEXTS.HTML) {
15181
        return htmlSanitizer(maybeTrusted);
15182
      }
15183
      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15184
    }
15185
 
15186
    return { trustAs: trustAs,
15187
             getTrusted: getTrusted,
15188
             valueOf: valueOf };
15189
  }];
15190
}
15191
 
15192
 
15193
/**
15194
 * @ngdoc provider
15195
 * @name $sceProvider
15196
 * @description
15197
 *
15198
 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
15199
 * -   enable/disable Strict Contextual Escaping (SCE) in a module
15200
 * -   override the default implementation with a custom delegate
15201
 *
15202
 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
15203
 */
15204
 
15205
/* jshint maxlen: false*/
15206
 
15207
/**
15208
 * @ngdoc service
15209
 * @name $sce
15210
 * @kind function
15211
 *
15212
 * @description
15213
 *
15214
 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
15215
 *
15216
 * # Strict Contextual Escaping
15217
 *
15218
 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
15219
 * contexts to result in a value that is marked as safe to use for that context.  One example of
15220
 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
15221
 * to these contexts as privileged or SCE contexts.
15222
 *
15223
 * As of version 1.2, Angular ships with SCE enabled by default.
15224
 *
15225
 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
15226
 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
15227
 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
15228
 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
15229
 * to the top of your HTML document.
15230
 *
15231
 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
15232
 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
15233
 *
15234
 * Here's an example of a binding in a privileged context:
15235
 *
15236
 * ```
15237
 * <input ng-model="userHtml">
15238
 * <div ng-bind-html="userHtml"></div>
15239
 * ```
15240
 *
15241
 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
15242
 * disabled, this application allows the user to render arbitrary HTML into the DIV.
15243
 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
15244
 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
15245
 * security vulnerabilities.)
15246
 *
15247
 * For the case of HTML, you might use a library, either on the client side, or on the server side,
15248
 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
15249
 *
15250
 * How would you ensure that every place that used these types of bindings was bound to a value that
15251
 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
15252
 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
15253
 * properties/fields and forgot to update the binding to the sanitized value?
15254
 *
15255
 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
15256
 * determine that something explicitly says it's safe to use a value for binding in that
15257
 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
15258
 * for those values that you can easily tell are safe - because they were received from your server,
15259
 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
15260
 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
15261
 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
15262
 *
15263
 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
15264
 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
15265
 * obtain values that will be accepted by SCE / privileged contexts.
15266
 *
15267
 *
15268
 * ## How does it work?
15269
 *
15270
 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
15271
 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
15272
 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
15273
 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
15274
 *
15275
 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
15276
 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
15277
 * simplified):
15278
 *
15279
 * ```
15280
 * var ngBindHtmlDirective = ['$sce', function($sce) {
15281
 *   return function(scope, element, attr) {
15282
 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
15283
 *       element.html(value || '');
15284
 *     });
15285
 *   };
15286
 * }];
15287
 * ```
15288
 *
15289
 * ## Impact on loading templates
15290
 *
15291
 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
15292
 * `templateUrl`'s specified by {@link guide/directive directives}.
15293
 *
15294
 * By default, Angular only loads templates from the same domain and protocol as the application
15295
 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
15296
 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
15297
 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
15298
 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
15299
 *
15300
 * *Please note*:
15301
 * The browser's
15302
 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
15303
 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
15304
 * policy apply in addition to this and may further restrict whether the template is successfully
15305
 * loaded.  This means that without the right CORS policy, loading templates from a different domain
15306
 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
15307
 * browsers.
15308
 *
15309
 * ## This feels like too much overhead
15310
 *
15311
 * It's important to remember that SCE only applies to interpolation expressions.
15312
 *
15313
 * If your expressions are constant literals, they're automatically trusted and you don't need to
15314
 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
15315
 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
15316
 *
15317
 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
15318
 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
15319
 *
15320
 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
15321
 * templates in `ng-include` from your application's domain without having to even know about SCE.
15322
 * It blocks loading templates from other domains or loading templates over http from an https
15323
 * served document.  You can change these by setting your own custom {@link
15324
 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
15325
 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
15326
 *
15327
 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
15328
 * application that's secure and can be audited to verify that with much more ease than bolting
15329
 * security onto an application later.
15330
 *
15331
 * <a name="contexts"></a>
15332
 * ## What trusted context types are supported?
15333
 *
15334
 * | Context             | Notes          |
15335
 * |---------------------|----------------|
15336
 * | `$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. |
15337
 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
15338
 * | `$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. |
15339
 * | `$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. |
15340
 * | `$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. |
15341
 *
15342
 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
15343
 *
15344
 *  Each element in these arrays must be one of the following:
15345
 *
15346
 *  - **'self'**
15347
 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
15348
 *      domain** as the application document using the **same protocol**.
15349
 *  - **String** (except the special value `'self'`)
15350
 *    - The string is matched against the full *normalized / absolute URL* of the resource
15351
 *      being tested (substring matches are not good enough.)
15352
 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
15353
 *      match themselves.
15354
 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
15355
 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'.  It's a useful wildcard for use
15356
 *      in a whitelist.
15357
 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
15358
 *      not appropriate to use in for a scheme, domain, etc. as it would match too much.  (e.g.
15359
 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
15360
 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
15361
 *      http://foo.example.com/templates/**).
15362
 *  - **RegExp** (*see caveat below*)
15363
 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
15364
 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
15365
 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
15366
 *      have good test coverage.).  For instance, the use of `.` in the regex is correct only in a
15367
 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
15368
 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
15369
 *      is highly recommended to use the string patterns and only fall back to regular expressions
15370
 *      if they as a last resort.
15371
 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
15372
 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
15373
 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
15374
 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
15375
 *    - If you are generating your JavaScript from some other templating engine (not
15376
 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
15377
 *      remember to escape your regular expression (and be aware that you might need more than
15378
 *      one level of escaping depending on your templating engine and the way you interpolated
15379
 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
15380
 *      enough before coding your own.  e.g. Ruby has
15381
 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
15382
 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
15383
 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
15384
 *      Closure library's [goog.string.regExpEscape(s)](
15385
 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
15386
 *
15387
 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
15388
 *
15389
 * ## Show me an example using SCE.
15390
 *
15391
 * <example module="mySceApp" deps="angular-sanitize.js">
15392
 * <file name="index.html">
15393
 *   <div ng-controller="AppController as myCtrl">
15394
 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
15395
 *     <b>User comments</b><br>
15396
 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
15397
 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
15398
 *     exploit.
15399
 *     <div class="well">
15400
 *       <div ng-repeat="userComment in myCtrl.userComments">
15401
 *         <b>{{userComment.name}}</b>:
15402
 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
15403
 *         <br>
15404
 *       </div>
15405
 *     </div>
15406
 *   </div>
15407
 * </file>
15408
 *
15409
 * <file name="script.js">
15410
 *   angular.module('mySceApp', ['ngSanitize'])
15411
 *     .controller('AppController', ['$http', '$templateCache', '$sce',
15412
 *       function($http, $templateCache, $sce) {
15413
 *         var self = this;
15414
 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
15415
 *           self.userComments = userComments;
15416
 *         });
15417
 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
15418
 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15419
 *             'sanitization.&quot;">Hover over this text.</span>');
15420
 *       }]);
15421
 * </file>
15422
 *
15423
 * <file name="test_data.json">
15424
 * [
15425
 *   { "name": "Alice",
15426
 *     "htmlComment":
15427
 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
15428
 *   },
15429
 *   { "name": "Bob",
15430
 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
15431
 *   }
15432
 * ]
15433
 * </file>
15434
 *
15435
 * <file name="protractor.js" type="protractor">
15436
 *   describe('SCE doc demo', function() {
15437
 *     it('should sanitize untrusted values', function() {
15438
 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
15439
 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
15440
 *     });
15441
 *
15442
 *     it('should NOT sanitize explicitly trusted values', function() {
15443
 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
15444
 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15445
 *           'sanitization.&quot;">Hover over this text.</span>');
15446
 *     });
15447
 *   });
15448
 * </file>
15449
 * </example>
15450
 *
15451
 *
15452
 *
15453
 * ## Can I disable SCE completely?
15454
 *
15455
 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
15456
 * for little coding overhead.  It will be much harder to take an SCE disabled application and
15457
 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
15458
 * for cases where you have a lot of existing code that was written before SCE was introduced and
15459
 * you're migrating them a module at a time.
15460
 *
15461
 * That said, here's how you can completely disable SCE:
15462
 *
15463
 * ```
15464
 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
15465
 *   // Completely disable SCE.  For demonstration purposes only!
15466
 *   // Do not use in new projects.
15467
 *   $sceProvider.enabled(false);
15468
 * });
15469
 * ```
15470
 *
15471
 */
15472
/* jshint maxlen: 100 */
15473
 
15474
function $SceProvider() {
15475
  var enabled = true;
15476
 
15477
  /**
15478
   * @ngdoc method
15479
   * @name $sceProvider#enabled
15480
   * @kind function
15481
   *
15482
   * @param {boolean=} value If provided, then enables/disables SCE.
15483
   * @return {boolean} true if SCE is enabled, false otherwise.
15484
   *
15485
   * @description
15486
   * Enables/disables SCE and returns the current value.
15487
   */
15488
  this.enabled = function(value) {
15489
    if (arguments.length) {
15490
      enabled = !!value;
15491
    }
15492
    return enabled;
15493
  };
15494
 
15495
 
15496
  /* Design notes on the default implementation for SCE.
15497
   *
15498
   * The API contract for the SCE delegate
15499
   * -------------------------------------
15500
   * The SCE delegate object must provide the following 3 methods:
15501
   *
15502
   * - trustAs(contextEnum, value)
15503
   *     This method is used to tell the SCE service that the provided value is OK to use in the
15504
   *     contexts specified by contextEnum.  It must return an object that will be accepted by
15505
   *     getTrusted() for a compatible contextEnum and return this value.
15506
   *
15507
   * - valueOf(value)
15508
   *     For values that were not produced by trustAs(), return them as is.  For values that were
15509
   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
15510
   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
15511
   *     such a value.
15512
   *
15513
   * - getTrusted(contextEnum, value)
15514
   *     This function should return the a value that is safe to use in the context specified by
15515
   *     contextEnum or throw and exception otherwise.
15516
   *
15517
   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
15518
   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
15519
   * instance, an implementation could maintain a registry of all trusted objects by context.  In
15520
   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
15521
   * return the same object passed in if it was found in the registry under a compatible context or
15522
   * throw an exception otherwise.  An implementation might only wrap values some of the time based
15523
   * on some criteria.  getTrusted() might return a value and not throw an exception for special
15524
   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
15525
   *
15526
   *
15527
   * A note on the inheritance model for SCE contexts
15528
   * ------------------------------------------------
15529
   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
15530
   * is purely an implementation details.
15531
   *
15532
   * The contract is simply this:
15533
   *
15534
   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
15535
   *     will also succeed.
15536
   *
15537
   * Inheritance happens to capture this in a natural way.  In some future, we
15538
   * may not use inheritance anymore.  That is OK because no code outside of
15539
   * sce.js and sceSpecs.js would need to be aware of this detail.
15540
   */
15541
 
15542
  this.$get = ['$parse', '$sceDelegate', function(
15543
                $parse,   $sceDelegate) {
15544
    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
15545
    // the "expression(javascript expression)" syntax which is insecure.
15546
    if (enabled && msie < 8) {
15547
      throw $sceMinErr('iequirks',
15548
        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
15549
        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
15550
        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
15551
    }
15552
 
15553
    var sce = shallowCopy(SCE_CONTEXTS);
15554
 
15555
    /**
15556
     * @ngdoc method
15557
     * @name $sce#isEnabled
15558
     * @kind function
15559
     *
15560
     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
15561
     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
15562
     *
15563
     * @description
15564
     * Returns a boolean indicating if SCE is enabled.
15565
     */
15566
    sce.isEnabled = function() {
15567
      return enabled;
15568
    };
15569
    sce.trustAs = $sceDelegate.trustAs;
15570
    sce.getTrusted = $sceDelegate.getTrusted;
15571
    sce.valueOf = $sceDelegate.valueOf;
15572
 
15573
    if (!enabled) {
15574
      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
15575
      sce.valueOf = identity;
15576
    }
15577
 
15578
    /**
15579
     * @ngdoc method
15580
     * @name $sce#parseAs
15581
     *
15582
     * @description
15583
     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
15584
     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
15585
     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
15586
     * *result*)}
15587
     *
15588
     * @param {string} type The kind of SCE context in which this result will be used.
15589
     * @param {string} expression String expression to compile.
15590
     * @returns {function(context, locals)} a function which represents the compiled expression:
15591
     *
15592
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15593
     *      are evaluated against (typically a scope object).
15594
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15595
     *      `context`.
15596
     */
15597
    sce.parseAs = function sceParseAs(type, expr) {
15598
      var parsed = $parse(expr);
15599
      if (parsed.literal && parsed.constant) {
15600
        return parsed;
15601
      } else {
15602
        return $parse(expr, function(value) {
15603
          return sce.getTrusted(type, value);
15604
        });
15605
      }
15606
    };
15607
 
15608
    /**
15609
     * @ngdoc method
15610
     * @name $sce#trustAs
15611
     *
15612
     * @description
15613
     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
15614
     * returns an object that is trusted by angular for use in specified strict contextual
15615
     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
15616
     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
15617
     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
15618
     * escaping.
15619
     *
15620
     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
15621
     *   resource_url, html, js and css.
15622
     * @param {*} value The value that that should be considered trusted/safe.
15623
     * @returns {*} A value that can be used to stand in for the provided `value` in places
15624
     * where Angular expects a $sce.trustAs() return value.
15625
     */
15626
 
15627
    /**
15628
     * @ngdoc method
15629
     * @name $sce#trustAsHtml
15630
     *
15631
     * @description
15632
     * Shorthand method.  `$sce.trustAsHtml(value)` →
15633
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
15634
     *
15635
     * @param {*} value The value to trustAs.
15636
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
15637
     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
15638
     *     only accept expressions that are either literal constants or are the
15639
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15640
     */
15641
 
15642
    /**
15643
     * @ngdoc method
15644
     * @name $sce#trustAsUrl
15645
     *
15646
     * @description
15647
     * Shorthand method.  `$sce.trustAsUrl(value)` →
15648
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
15649
     *
15650
     * @param {*} value The value to trustAs.
15651
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
15652
     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
15653
     *     only accept expressions that are either literal constants or are the
15654
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15655
     */
15656
 
15657
    /**
15658
     * @ngdoc method
15659
     * @name $sce#trustAsResourceUrl
15660
     *
15661
     * @description
15662
     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
15663
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
15664
     *
15665
     * @param {*} value The value to trustAs.
15666
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
15667
     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
15668
     *     only accept expressions that are either literal constants or are the return
15669
     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
15670
     */
15671
 
15672
    /**
15673
     * @ngdoc method
15674
     * @name $sce#trustAsJs
15675
     *
15676
     * @description
15677
     * Shorthand method.  `$sce.trustAsJs(value)` →
15678
     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
15679
     *
15680
     * @param {*} value The value to trustAs.
15681
     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
15682
     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
15683
     *     only accept expressions that are either literal constants or are the
15684
     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15685
     */
15686
 
15687
    /**
15688
     * @ngdoc method
15689
     * @name $sce#getTrusted
15690
     *
15691
     * @description
15692
     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
15693
     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
15694
     * originally supplied value if the queried context type is a supertype of the created type.
15695
     * If this condition isn't satisfied, throws an exception.
15696
     *
15697
     * @param {string} type The kind of context in which this value is to be used.
15698
     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
15699
     *                         call.
15700
     * @returns {*} The value the was originally provided to
15701
     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
15702
     *              Otherwise, throws an exception.
15703
     */
15704
 
15705
    /**
15706
     * @ngdoc method
15707
     * @name $sce#getTrustedHtml
15708
     *
15709
     * @description
15710
     * Shorthand method.  `$sce.getTrustedHtml(value)` →
15711
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
15712
     *
15713
     * @param {*} value The value to pass to `$sce.getTrusted`.
15714
     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
15715
     */
15716
 
15717
    /**
15718
     * @ngdoc method
15719
     * @name $sce#getTrustedCss
15720
     *
15721
     * @description
15722
     * Shorthand method.  `$sce.getTrustedCss(value)` →
15723
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
15724
     *
15725
     * @param {*} value The value to pass to `$sce.getTrusted`.
15726
     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
15727
     */
15728
 
15729
    /**
15730
     * @ngdoc method
15731
     * @name $sce#getTrustedUrl
15732
     *
15733
     * @description
15734
     * Shorthand method.  `$sce.getTrustedUrl(value)` →
15735
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
15736
     *
15737
     * @param {*} value The value to pass to `$sce.getTrusted`.
15738
     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
15739
     */
15740
 
15741
    /**
15742
     * @ngdoc method
15743
     * @name $sce#getTrustedResourceUrl
15744
     *
15745
     * @description
15746
     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
15747
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
15748
     *
15749
     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
15750
     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
15751
     */
15752
 
15753
    /**
15754
     * @ngdoc method
15755
     * @name $sce#getTrustedJs
15756
     *
15757
     * @description
15758
     * Shorthand method.  `$sce.getTrustedJs(value)` →
15759
     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
15760
     *
15761
     * @param {*} value The value to pass to `$sce.getTrusted`.
15762
     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
15763
     */
15764
 
15765
    /**
15766
     * @ngdoc method
15767
     * @name $sce#parseAsHtml
15768
     *
15769
     * @description
15770
     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
15771
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
15772
     *
15773
     * @param {string} expression String expression to compile.
15774
     * @returns {function(context, locals)} a function which represents the compiled expression:
15775
     *
15776
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15777
     *      are evaluated against (typically a scope object).
15778
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15779
     *      `context`.
15780
     */
15781
 
15782
    /**
15783
     * @ngdoc method
15784
     * @name $sce#parseAsCss
15785
     *
15786
     * @description
15787
     * Shorthand method.  `$sce.parseAsCss(value)` →
15788
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
15789
     *
15790
     * @param {string} expression String expression to compile.
15791
     * @returns {function(context, locals)} a function which represents the compiled expression:
15792
     *
15793
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15794
     *      are evaluated against (typically a scope object).
15795
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15796
     *      `context`.
15797
     */
15798
 
15799
    /**
15800
     * @ngdoc method
15801
     * @name $sce#parseAsUrl
15802
     *
15803
     * @description
15804
     * Shorthand method.  `$sce.parseAsUrl(value)` →
15805
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
15806
     *
15807
     * @param {string} expression String expression to compile.
15808
     * @returns {function(context, locals)} a function which represents the compiled expression:
15809
     *
15810
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15811
     *      are evaluated against (typically a scope object).
15812
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15813
     *      `context`.
15814
     */
15815
 
15816
    /**
15817
     * @ngdoc method
15818
     * @name $sce#parseAsResourceUrl
15819
     *
15820
     * @description
15821
     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
15822
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
15823
     *
15824
     * @param {string} expression String expression to compile.
15825
     * @returns {function(context, locals)} a function which represents the compiled expression:
15826
     *
15827
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15828
     *      are evaluated against (typically a scope object).
15829
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15830
     *      `context`.
15831
     */
15832
 
15833
    /**
15834
     * @ngdoc method
15835
     * @name $sce#parseAsJs
15836
     *
15837
     * @description
15838
     * Shorthand method.  `$sce.parseAsJs(value)` →
15839
     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
15840
     *
15841
     * @param {string} expression String expression to compile.
15842
     * @returns {function(context, locals)} a function which represents the compiled expression:
15843
     *
15844
     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
15845
     *      are evaluated against (typically a scope object).
15846
     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
15847
     *      `context`.
15848
     */
15849
 
15850
    // Shorthand delegations.
15851
    var parse = sce.parseAs,
15852
        getTrusted = sce.getTrusted,
15853
        trustAs = sce.trustAs;
15854
 
15855
    forEach(SCE_CONTEXTS, function(enumValue, name) {
15856
      var lName = lowercase(name);
15857
      sce[camelCase("parse_as_" + lName)] = function(expr) {
15858
        return parse(enumValue, expr);
15859
      };
15860
      sce[camelCase("get_trusted_" + lName)] = function(value) {
15861
        return getTrusted(enumValue, value);
15862
      };
15863
      sce[camelCase("trust_as_" + lName)] = function(value) {
15864
        return trustAs(enumValue, value);
15865
      };
15866
    });
15867
 
15868
    return sce;
15869
  }];
15870
}
15871
 
15872
/**
15873
 * !!! This is an undocumented "private" service !!!
15874
 *
15875
 * @name $sniffer
15876
 * @requires $window
15877
 * @requires $document
15878
 *
15879
 * @property {boolean} history Does the browser support html5 history api ?
15880
 * @property {boolean} transitions Does the browser support CSS transition events ?
15881
 * @property {boolean} animations Does the browser support CSS animation events ?
15882
 *
15883
 * @description
15884
 * This is very simple implementation of testing browser's features.
15885
 */
15886
function $SnifferProvider() {
15887
  this.$get = ['$window', '$document', function($window, $document) {
15888
    var eventSupport = {},
15889
        android =
15890
          int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
15891
        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
15892
        document = $document[0] || {},
15893
        vendorPrefix,
15894
        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
15895
        bodyStyle = document.body && document.body.style,
15896
        transitions = false,
15897
        animations = false,
15898
        match;
15899
 
15900
    if (bodyStyle) {
15901
      for (var prop in bodyStyle) {
15902
        if (match = vendorRegex.exec(prop)) {
15903
          vendorPrefix = match[0];
15904
          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
15905
          break;
15906
        }
15907
      }
15908
 
15909
      if (!vendorPrefix) {
15910
        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
15911
      }
15912
 
15913
      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
15914
      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
15915
 
15916
      if (android && (!transitions ||  !animations)) {
15917
        transitions = isString(document.body.style.webkitTransition);
15918
        animations = isString(document.body.style.webkitAnimation);
15919
      }
15920
    }
15921
 
15922
 
15923
    return {
15924
      // Android has history.pushState, but it does not update location correctly
15925
      // so let's not use the history API at all.
15926
      // http://code.google.com/p/android/issues/detail?id=17471
15927
      // https://github.com/angular/angular.js/issues/904
15928
 
15929
      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
15930
      // so let's not use the history API also
15931
      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
15932
      // jshint -W018
15933
      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
15934
      // jshint +W018
15935
      hasEvent: function(event) {
15936
        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
15937
        // it. In particular the event is not fired when backspace or delete key are pressed or
15938
        // when cut operation is performed.
15939
        // IE10+ implements 'input' event but it erroneously fires under various situations,
15940
        // e.g. when placeholder changes, or a form is focused.
15941
        if (event === 'input' && msie <= 11) return false;
15942
 
15943
        if (isUndefined(eventSupport[event])) {
15944
          var divElm = document.createElement('div');
15945
          eventSupport[event] = 'on' + event in divElm;
15946
        }
15947
 
15948
        return eventSupport[event];
15949
      },
15950
      csp: csp(),
15951
      vendorPrefix: vendorPrefix,
15952
      transitions: transitions,
15953
      animations: animations,
15954
      android: android
15955
    };
15956
  }];
15957
}
15958
 
15959
var $compileMinErr = minErr('$compile');
15960
 
15961
/**
15962
 * @ngdoc service
15963
 * @name $templateRequest
15964
 *
15965
 * @description
15966
 * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
15967
 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
15968
 * of the HTTP request is empty then a `$compile` error will be thrown (the exception can be thwarted
15969
 * by setting the 2nd parameter of the function to true).
15970
 *
15971
 * @param {string} tpl The HTTP request template URL
15972
 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
15973
 *
15974
 * @return {Promise} the HTTP Promise for the given.
15975
 *
15976
 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
15977
 */
15978
function $TemplateRequestProvider() {
15979
  this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
15980
    function handleRequestFn(tpl, ignoreRequestError) {
15981
      var self = handleRequestFn;
15982
      self.totalPendingRequests++;
15983
 
15984
      var transformResponse = $http.defaults && $http.defaults.transformResponse;
15985
 
15986
      if (isArray(transformResponse)) {
15987
        transformResponse = transformResponse.filter(function(transformer) {
15988
          return transformer !== defaultHttpResponseTransform;
15989
        });
15990
      } else if (transformResponse === defaultHttpResponseTransform) {
15991
        transformResponse = null;
15992
      }
15993
 
15994
      var httpOptions = {
15995
        cache: $templateCache,
15996
        transformResponse: transformResponse
15997
      };
15998
 
15999
      return $http.get(tpl, httpOptions)
16000
        .then(function(response) {
16001
          self.totalPendingRequests--;
16002
          return response.data;
16003
        }, handleError);
16004
 
16005
      function handleError(resp) {
16006
        self.totalPendingRequests--;
16007
        if (!ignoreRequestError) {
16008
          throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
16009
        }
16010
        return $q.reject(resp);
16011
      }
16012
    }
16013
 
16014
    handleRequestFn.totalPendingRequests = 0;
16015
 
16016
    return handleRequestFn;
16017
  }];
16018
}
16019
 
16020
function $$TestabilityProvider() {
16021
  this.$get = ['$rootScope', '$browser', '$location',
16022
       function($rootScope,   $browser,   $location) {
16023
 
16024
    /**
16025
     * @name $testability
16026
     *
16027
     * @description
16028
     * The private $$testability service provides a collection of methods for use when debugging
16029
     * or by automated test and debugging tools.
16030
     */
16031
    var testability = {};
16032
 
16033
    /**
16034
     * @name $$testability#findBindings
16035
     *
16036
     * @description
16037
     * Returns an array of elements that are bound (via ng-bind or {{}})
16038
     * to expressions matching the input.
16039
     *
16040
     * @param {Element} element The element root to search from.
16041
     * @param {string} expression The binding expression to match.
16042
     * @param {boolean} opt_exactMatch If true, only returns exact matches
16043
     *     for the expression. Filters and whitespace are ignored.
16044
     */
16045
    testability.findBindings = function(element, expression, opt_exactMatch) {
16046
      var bindings = element.getElementsByClassName('ng-binding');
16047
      var matches = [];
16048
      forEach(bindings, function(binding) {
16049
        var dataBinding = angular.element(binding).data('$binding');
16050
        if (dataBinding) {
16051
          forEach(dataBinding, function(bindingName) {
16052
            if (opt_exactMatch) {
16053
              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
16054
              if (matcher.test(bindingName)) {
16055
                matches.push(binding);
16056
              }
16057
            } else {
16058
              if (bindingName.indexOf(expression) != -1) {
16059
                matches.push(binding);
16060
              }
16061
            }
16062
          });
16063
        }
16064
      });
16065
      return matches;
16066
    };
16067
 
16068
    /**
16069
     * @name $$testability#findModels
16070
     *
16071
     * @description
16072
     * Returns an array of elements that are two-way found via ng-model to
16073
     * expressions matching the input.
16074
     *
16075
     * @param {Element} element The element root to search from.
16076
     * @param {string} expression The model expression to match.
16077
     * @param {boolean} opt_exactMatch If true, only returns exact matches
16078
     *     for the expression.
16079
     */
16080
    testability.findModels = function(element, expression, opt_exactMatch) {
16081
      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
16082
      for (var p = 0; p < prefixes.length; ++p) {
16083
        var attributeEquals = opt_exactMatch ? '=' : '*=';
16084
        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
16085
        var elements = element.querySelectorAll(selector);
16086
        if (elements.length) {
16087
          return elements;
16088
        }
16089
      }
16090
    };
16091
 
16092
    /**
16093
     * @name $$testability#getLocation
16094
     *
16095
     * @description
16096
     * Shortcut for getting the location in a browser agnostic way. Returns
16097
     *     the path, search, and hash. (e.g. /path?a=b#hash)
16098
     */
16099
    testability.getLocation = function() {
16100
      return $location.url();
16101
    };
16102
 
16103
    /**
16104
     * @name $$testability#setLocation
16105
     *
16106
     * @description
16107
     * Shortcut for navigating to a location without doing a full page reload.
16108
     *
16109
     * @param {string} url The location url (path, search and hash,
16110
     *     e.g. /path?a=b#hash) to go to.
16111
     */
16112
    testability.setLocation = function(url) {
16113
      if (url !== $location.url()) {
16114
        $location.url(url);
16115
        $rootScope.$digest();
16116
      }
16117
    };
16118
 
16119
    /**
16120
     * @name $$testability#whenStable
16121
     *
16122
     * @description
16123
     * Calls the callback when $timeout and $http requests are completed.
16124
     *
16125
     * @param {function} callback
16126
     */
16127
    testability.whenStable = function(callback) {
16128
      $browser.notifyWhenNoOutstandingRequests(callback);
16129
    };
16130
 
16131
    return testability;
16132
  }];
16133
}
16134
 
16135
function $TimeoutProvider() {
16136
  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
16137
       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
16138
    var deferreds = {};
16139
 
16140
 
16141
     /**
16142
      * @ngdoc service
16143
      * @name $timeout
16144
      *
16145
      * @description
16146
      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16147
      * block and delegates any exceptions to
16148
      * {@link ng.$exceptionHandler $exceptionHandler} service.
16149
      *
16150
      * The return value of registering a timeout function is a promise, which will be resolved when
16151
      * the timeout is reached and the timeout function is executed.
16152
      *
16153
      * To cancel a timeout request, call `$timeout.cancel(promise)`.
16154
      *
16155
      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
16156
      * synchronously flush the queue of deferred functions.
16157
      *
16158
      * @param {function()} fn A function, whose execution should be delayed.
16159
      * @param {number=} [delay=0] Delay in milliseconds.
16160
      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
16161
      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
16162
      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
16163
      *   promise will be resolved with is the return value of the `fn` function.
16164
      *
16165
      */
16166
    function timeout(fn, delay, invokeApply) {
16167
      var skipApply = (isDefined(invokeApply) && !invokeApply),
16168
          deferred = (skipApply ? $$q : $q).defer(),
16169
          promise = deferred.promise,
16170
          timeoutId;
16171
 
16172
      timeoutId = $browser.defer(function() {
16173
        try {
16174
          deferred.resolve(fn());
16175
        } catch (e) {
16176
          deferred.reject(e);
16177
          $exceptionHandler(e);
16178
        }
16179
        finally {
16180
          delete deferreds[promise.$$timeoutId];
16181
        }
16182
 
16183
        if (!skipApply) $rootScope.$apply();
16184
      }, delay);
16185
 
16186
      promise.$$timeoutId = timeoutId;
16187
      deferreds[timeoutId] = deferred;
16188
 
16189
      return promise;
16190
    }
16191
 
16192
 
16193
     /**
16194
      * @ngdoc method
16195
      * @name $timeout#cancel
16196
      *
16197
      * @description
16198
      * Cancels a task associated with the `promise`. As a result of this, the promise will be
16199
      * resolved with a rejection.
16200
      *
16201
      * @param {Promise=} promise Promise returned by the `$timeout` function.
16202
      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
16203
      *   canceled.
16204
      */
16205
    timeout.cancel = function(promise) {
16206
      if (promise && promise.$$timeoutId in deferreds) {
16207
        deferreds[promise.$$timeoutId].reject('canceled');
16208
        delete deferreds[promise.$$timeoutId];
16209
        return $browser.defer.cancel(promise.$$timeoutId);
16210
      }
16211
      return false;
16212
    };
16213
 
16214
    return timeout;
16215
  }];
16216
}
16217
 
16218
// NOTE:  The usage of window and document instead of $window and $document here is
16219
// deliberate.  This service depends on the specific behavior of anchor nodes created by the
16220
// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
16221
// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
16222
// doesn't know about mocked locations and resolves URLs to the real document - which is
16223
// exactly the behavior needed here.  There is little value is mocking these out for this
16224
// service.
16225
var urlParsingNode = document.createElement("a");
16226
var originUrl = urlResolve(window.location.href);
16227
 
16228
 
16229
/**
16230
 *
16231
 * Implementation Notes for non-IE browsers
16232
 * ----------------------------------------
16233
 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
16234
 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
16235
 * URL will be resolved into an absolute URL in the context of the application document.
16236
 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
16237
 * properties are all populated to reflect the normalized URL.  This approach has wide
16238
 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
16239
 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16240
 *
16241
 * Implementation Notes for IE
16242
 * ---------------------------
16243
 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
16244
 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
16245
 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
16246
 * work around that by performing the parsing in a 2nd step by taking a previously normalized
16247
 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
16248
 * properties such as protocol, hostname, port, etc.
16249
 *
16250
 * IE7 does not normalize the URL when assigned to an anchor node.  (Apparently, it does, if one
16251
 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
16252
 * http://stackoverflow.com/a/472729)  However, setting img[src] does normalize the URL.
16253
 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
16254
 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
16255
 * method and IE < 8 is unsupported.
16256
 *
16257
 * References:
16258
 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
16259
 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16260
 *   http://url.spec.whatwg.org/#urlutils
16261
 *   https://github.com/angular/angular.js/pull/2902
16262
 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
16263
 *
16264
 * @kind function
16265
 * @param {string} url The URL to be parsed.
16266
 * @description Normalizes and parses a URL.
16267
 * @returns {object} Returns the normalized URL as a dictionary.
16268
 *
16269
 *   | member name   | Description    |
16270
 *   |---------------|----------------|
16271
 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
16272
 *   | protocol      | The protocol including the trailing colon                              |
16273
 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
16274
 *   | search        | The search params, minus the question mark                             |
16275
 *   | hash          | The hash string, minus the hash symbol
16276
 *   | hostname      | The hostname
16277
 *   | port          | The port, without ":"
16278
 *   | pathname      | The pathname, beginning with "/"
16279
 *
16280
 */
16281
function urlResolve(url) {
16282
  var href = url;
16283
 
16284
  if (msie) {
16285
    // Normalize before parse.  Refer Implementation Notes on why this is
16286
    // done in two steps on IE.
16287
    urlParsingNode.setAttribute("href", href);
16288
    href = urlParsingNode.href;
16289
  }
16290
 
16291
  urlParsingNode.setAttribute('href', href);
16292
 
16293
  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
16294
  return {
16295
    href: urlParsingNode.href,
16296
    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
16297
    host: urlParsingNode.host,
16298
    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
16299
    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
16300
    hostname: urlParsingNode.hostname,
16301
    port: urlParsingNode.port,
16302
    pathname: (urlParsingNode.pathname.charAt(0) === '/')
16303
      ? urlParsingNode.pathname
16304
      : '/' + urlParsingNode.pathname
16305
  };
16306
}
16307
 
16308
/**
16309
 * Parse a request URL and determine whether this is a same-origin request as the application document.
16310
 *
16311
 * @param {string|object} requestUrl The url of the request as a string that will be resolved
16312
 * or a parsed URL object.
16313
 * @returns {boolean} Whether the request is for the same origin as the application document.
16314
 */
16315
function urlIsSameOrigin(requestUrl) {
16316
  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
16317
  return (parsed.protocol === originUrl.protocol &&
16318
          parsed.host === originUrl.host);
16319
}
16320
 
16321
/**
16322
 * @ngdoc service
16323
 * @name $window
16324
 *
16325
 * @description
16326
 * A reference to the browser's `window` object. While `window`
16327
 * is globally available in JavaScript, it causes testability problems, because
16328
 * it is a global variable. In angular we always refer to it through the
16329
 * `$window` service, so it may be overridden, removed or mocked for testing.
16330
 *
16331
 * Expressions, like the one defined for the `ngClick` directive in the example
16332
 * below, are evaluated with respect to the current scope.  Therefore, there is
16333
 * no risk of inadvertently coding in a dependency on a global value in such an
16334
 * expression.
16335
 *
16336
 * @example
16337
   <example module="windowExample">
16338
     <file name="index.html">
16339
       <script>
16340
         angular.module('windowExample', [])
16341
           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
16342
             $scope.greeting = 'Hello, World!';
16343
             $scope.doGreeting = function(greeting) {
16344
               $window.alert(greeting);
16345
             };
16346
           }]);
16347
       </script>
16348
       <div ng-controller="ExampleController">
16349
         <input type="text" ng-model="greeting" />
16350
         <button ng-click="doGreeting(greeting)">ALERT</button>
16351
       </div>
16352
     </file>
16353
     <file name="protractor.js" type="protractor">
16354
      it('should display the greeting in the input box', function() {
16355
       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
16356
       // If we click the button it will block the test runner
16357
       // element(':button').click();
16358
      });
16359
     </file>
16360
   </example>
16361
 */
16362
function $WindowProvider() {
16363
  this.$get = valueFn(window);
16364
}
16365
 
16366
/* global currencyFilter: true,
16367
 dateFilter: true,
16368
 filterFilter: true,
16369
 jsonFilter: true,
16370
 limitToFilter: true,
16371
 lowercaseFilter: true,
16372
 numberFilter: true,
16373
 orderByFilter: true,
16374
 uppercaseFilter: true,
16375
 */
16376
 
16377
/**
16378
 * @ngdoc provider
16379
 * @name $filterProvider
16380
 * @description
16381
 *
16382
 * Filters are just functions which transform input to an output. However filters need to be
16383
 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
16384
 * annotated with dependencies and is responsible for creating a filter function.
16385
 *
16386
 * ```js
16387
 *   // Filter registration
16388
 *   function MyModule($provide, $filterProvider) {
16389
 *     // create a service to demonstrate injection (not always needed)
16390
 *     $provide.value('greet', function(name){
16391
 *       return 'Hello ' + name + '!';
16392
 *     });
16393
 *
16394
 *     // register a filter factory which uses the
16395
 *     // greet service to demonstrate DI.
16396
 *     $filterProvider.register('greet', function(greet){
16397
 *       // return the filter function which uses the greet service
16398
 *       // to generate salutation
16399
 *       return function(text) {
16400
 *         // filters need to be forgiving so check input validity
16401
 *         return text && greet(text) || text;
16402
 *       };
16403
 *     });
16404
 *   }
16405
 * ```
16406
 *
16407
 * The filter function is registered with the `$injector` under the filter name suffix with
16408
 * `Filter`.
16409
 *
16410
 * ```js
16411
 *   it('should be the same instance', inject(
16412
 *     function($filterProvider) {
16413
 *       $filterProvider.register('reverse', function(){
16414
 *         return ...;
16415
 *       });
16416
 *     },
16417
 *     function($filter, reverseFilter) {
16418
 *       expect($filter('reverse')).toBe(reverseFilter);
16419
 *     });
16420
 * ```
16421
 *
16422
 *
16423
 * For more information about how angular filters work, and how to create your own filters, see
16424
 * {@link guide/filter Filters} in the Angular Developer Guide.
16425
 */
16426
 
16427
/**
16428
 * @ngdoc service
16429
 * @name $filter
16430
 * @kind function
16431
 * @description
16432
 * Filters are used for formatting data displayed to the user.
16433
 *
16434
 * The general syntax in templates is as follows:
16435
 *
16436
 *         {{ expression [| filter_name[:parameter_value] ... ] }}
16437
 *
16438
 * @param {String} name Name of the filter function to retrieve
16439
 * @return {Function} the filter function
16440
 * @example
16441
   <example name="$filter" module="filterExample">
16442
     <file name="index.html">
16443
       <div ng-controller="MainCtrl">
16444
        <h3>{{ originalText }}</h3>
16445
        <h3>{{ filteredText }}</h3>
16446
       </div>
16447
     </file>
16448
 
16449
     <file name="script.js">
16450
      angular.module('filterExample', [])
16451
      .controller('MainCtrl', function($scope, $filter) {
16452
        $scope.originalText = 'hello';
16453
        $scope.filteredText = $filter('uppercase')($scope.originalText);
16454
      });
16455
     </file>
16456
   </example>
16457
  */
16458
$FilterProvider.$inject = ['$provide'];
16459
function $FilterProvider($provide) {
16460
  var suffix = 'Filter';
16461
 
16462
  /**
16463
   * @ngdoc method
16464
   * @name $filterProvider#register
16465
   * @param {string|Object} name Name of the filter function, or an object map of filters where
16466
   *    the keys are the filter names and the values are the filter factories.
16467
   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
16468
   *    of the registered filter instances.
16469
   */
16470
  function register(name, factory) {
16471
    if (isObject(name)) {
16472
      var filters = {};
16473
      forEach(name, function(filter, key) {
16474
        filters[key] = register(key, filter);
16475
      });
16476
      return filters;
16477
    } else {
16478
      return $provide.factory(name + suffix, factory);
16479
    }
16480
  }
16481
  this.register = register;
16482
 
16483
  this.$get = ['$injector', function($injector) {
16484
    return function(name) {
16485
      return $injector.get(name + suffix);
16486
    };
16487
  }];
16488
 
16489
  ////////////////////////////////////////
16490
 
16491
  /* global
16492
    currencyFilter: false,
16493
    dateFilter: false,
16494
    filterFilter: false,
16495
    jsonFilter: false,
16496
    limitToFilter: false,
16497
    lowercaseFilter: false,
16498
    numberFilter: false,
16499
    orderByFilter: false,
16500
    uppercaseFilter: false,
16501
  */
16502
 
16503
  register('currency', currencyFilter);
16504
  register('date', dateFilter);
16505
  register('filter', filterFilter);
16506
  register('json', jsonFilter);
16507
  register('limitTo', limitToFilter);
16508
  register('lowercase', lowercaseFilter);
16509
  register('number', numberFilter);
16510
  register('orderBy', orderByFilter);
16511
  register('uppercase', uppercaseFilter);
16512
}
16513
 
16514
/**
16515
 * @ngdoc filter
16516
 * @name filter
16517
 * @kind function
16518
 *
16519
 * @description
16520
 * Selects a subset of items from `array` and returns it as a new array.
16521
 *
16522
 * @param {Array} array The source array.
16523
 * @param {string|Object|function()} expression The predicate to be used for selecting items from
16524
 *   `array`.
16525
 *
16526
 *   Can be one of:
16527
 *
16528
 *   - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
16529
 *     the contents of the `array`. All strings or objects with string properties in `array` that contain this string
16530
 *     will be returned. The predicate can be negated by prefixing the string with `!`.
16531
 *
16532
 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
16533
 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
16534
 *     which have property `name` containing "M" and property `phone` containing "1". A special
16535
 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
16536
 *     property of the object. That's equivalent to the simple substring match with a `string`
16537
 *     as described above. The predicate can be negated by prefixing the string with `!`.
16538
 *     For Example `{name: "!M"}` predicate will return an array of items which have property `name`
16539
 *     not containing "M".
16540
 *
16541
 *   - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
16542
 *     function is called for each element of `array`. The final result is an array of those
16543
 *     elements that the predicate returned true for.
16544
 *
16545
 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
16546
 *     determining if the expected value (from the filter expression) and actual value (from
16547
 *     the object in the array) should be considered a match.
16548
 *
16549
 *   Can be one of:
16550
 *
16551
 *   - `function(actual, expected)`:
16552
 *     The function will be given the object value and the predicate value to compare and
16553
 *     should return true if the item should be included in filtered result.
16554
 *
16555
 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
16556
 *     this is essentially strict comparison of expected and actual.
16557
 *
16558
 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
16559
 *     insensitive way.
16560
 *
16561
 * @example
16562
   <example>
16563
     <file name="index.html">
16564
       <div ng-init="friends = [{name:'John', phone:'555-1276'},
16565
                                {name:'Mary', phone:'800-BIG-MARY'},
16566
                                {name:'Mike', phone:'555-4321'},
16567
                                {name:'Adam', phone:'555-5678'},
16568
                                {name:'Julie', phone:'555-8765'},
16569
                                {name:'Juliette', phone:'555-5678'}]"></div>
16570
 
16571
       Search: <input ng-model="searchText">
16572
       <table id="searchTextResults">
16573
         <tr><th>Name</th><th>Phone</th></tr>
16574
         <tr ng-repeat="friend in friends | filter:searchText">
16575
           <td>{{friend.name}}</td>
16576
           <td>{{friend.phone}}</td>
16577
         </tr>
16578
       </table>
16579
       <hr>
16580
       Any: <input ng-model="search.$"> <br>
16581
       Name only <input ng-model="search.name"><br>
16582
       Phone only <input ng-model="search.phone"><br>
16583
       Equality <input type="checkbox" ng-model="strict"><br>
16584
       <table id="searchObjResults">
16585
         <tr><th>Name</th><th>Phone</th></tr>
16586
         <tr ng-repeat="friendObj in friends | filter:search:strict">
16587
           <td>{{friendObj.name}}</td>
16588
           <td>{{friendObj.phone}}</td>
16589
         </tr>
16590
       </table>
16591
     </file>
16592
     <file name="protractor.js" type="protractor">
16593
       var expectFriendNames = function(expectedNames, key) {
16594
         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
16595
           arr.forEach(function(wd, i) {
16596
             expect(wd.getText()).toMatch(expectedNames[i]);
16597
           });
16598
         });
16599
       };
16600
 
16601
       it('should search across all fields when filtering with a string', function() {
16602
         var searchText = element(by.model('searchText'));
16603
         searchText.clear();
16604
         searchText.sendKeys('m');
16605
         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
16606
 
16607
         searchText.clear();
16608
         searchText.sendKeys('76');
16609
         expectFriendNames(['John', 'Julie'], 'friend');
16610
       });
16611
 
16612
       it('should search in specific fields when filtering with a predicate object', function() {
16613
         var searchAny = element(by.model('search.$'));
16614
         searchAny.clear();
16615
         searchAny.sendKeys('i');
16616
         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
16617
       });
16618
       it('should use a equal comparison when comparator is true', function() {
16619
         var searchName = element(by.model('search.name'));
16620
         var strict = element(by.model('strict'));
16621
         searchName.clear();
16622
         searchName.sendKeys('Julie');
16623
         strict.click();
16624
         expectFriendNames(['Julie'], 'friendObj');
16625
       });
16626
     </file>
16627
   </example>
16628
 */
16629
function filterFilter() {
16630
  return function(array, expression, comparator) {
16631
    if (!isArray(array)) return array;
16632
 
16633
    var predicateFn;
16634
    var matchAgainstAnyProp;
16635
 
16636
    switch (typeof expression) {
16637
      case 'function':
16638
        predicateFn = expression;
16639
        break;
16640
      case 'boolean':
16641
      case 'number':
16642
      case 'string':
16643
        matchAgainstAnyProp = true;
16644
        //jshint -W086
16645
      case 'object':
16646
        //jshint +W086
16647
        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
16648
        break;
16649
      default:
16650
        return array;
16651
    }
16652
 
16653
    return array.filter(predicateFn);
16654
  };
16655
}
16656
 
16657
// Helper functions for `filterFilter`
16658
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
16659
  var predicateFn;
16660
 
16661
  if (comparator === true) {
16662
    comparator = equals;
16663
  } else if (!isFunction(comparator)) {
16664
    comparator = function(actual, expected) {
16665
      if (isObject(actual) || isObject(expected)) {
16666
        // Prevent an object to be considered equal to a string like `'[object'`
16667
        return false;
16668
      }
16669
 
16670
      actual = lowercase('' + actual);
16671
      expected = lowercase('' + expected);
16672
      return actual.indexOf(expected) !== -1;
16673
    };
16674
  }
16675
 
16676
  predicateFn = function(item) {
16677
    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
16678
  };
16679
 
16680
  return predicateFn;
16681
}
16682
 
16683
function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
16684
  var actualType = typeof actual;
16685
  var expectedType = typeof expected;
16686
 
16687
  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
16688
    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
16689
  } else if (actualType === 'array') {
16690
    // In case `actual` is an array, consider it a match
16691
    // if ANY of it's items matches `expected`
16692
    return actual.some(function(item) {
16693
      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
16694
    });
16695
  }
16696
 
16697
  switch (actualType) {
16698
    case 'object':
16699
      var key;
16700
      if (matchAgainstAnyProp) {
16701
        for (key in actual) {
16702
          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) {
16703
            return true;
16704
          }
16705
        }
16706
        return false;
16707
      } else if (expectedType === 'object') {
16708
        for (key in expected) {
16709
          var expectedVal = expected[key];
16710
          if (isFunction(expectedVal)) {
16711
            continue;
16712
          }
16713
 
16714
          var keyIsDollar = key === '$';
16715
          var actualVal = keyIsDollar ? actual : actual[key];
16716
          if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) {
16717
            return false;
16718
          }
16719
        }
16720
        return true;
16721
      } else {
16722
        return comparator(actual, expected);
16723
      }
16724
      break;
16725
    case 'function':
16726
      return false;
16727
    default:
16728
      return comparator(actual, expected);
16729
  }
16730
}
16731
 
16732
/**
16733
 * @ngdoc filter
16734
 * @name currency
16735
 * @kind function
16736
 *
16737
 * @description
16738
 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
16739
 * symbol for current locale is used.
16740
 *
16741
 * @param {number} amount Input to filter.
16742
 * @param {string=} symbol Currency symbol or identifier to be displayed.
16743
 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
16744
 * @returns {string} Formatted number.
16745
 *
16746
 *
16747
 * @example
16748
   <example module="currencyExample">
16749
     <file name="index.html">
16750
       <script>
16751
         angular.module('currencyExample', [])
16752
           .controller('ExampleController', ['$scope', function($scope) {
16753
             $scope.amount = 1234.56;
16754
           }]);
16755
       </script>
16756
       <div ng-controller="ExampleController">
16757
         <input type="number" ng-model="amount"> <br>
16758
         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
16759
         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
16760
         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
16761
       </div>
16762
     </file>
16763
     <file name="protractor.js" type="protractor">
16764
       it('should init with 1234.56', function() {
16765
         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
16766
         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
16767
         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
16768
       });
16769
       it('should update', function() {
16770
         if (browser.params.browser == 'safari') {
16771
           // Safari does not understand the minus key. See
16772
           // https://github.com/angular/protractor/issues/481
16773
           return;
16774
         }
16775
         element(by.model('amount')).clear();
16776
         element(by.model('amount')).sendKeys('-1234');
16777
         expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
16778
         expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
16779
         expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
16780
       });
16781
     </file>
16782
   </example>
16783
 */
16784
currencyFilter.$inject = ['$locale'];
16785
function currencyFilter($locale) {
16786
  var formats = $locale.NUMBER_FORMATS;
16787
  return function(amount, currencySymbol, fractionSize) {
16788
    if (isUndefined(currencySymbol)) {
16789
      currencySymbol = formats.CURRENCY_SYM;
16790
    }
16791
 
16792
    if (isUndefined(fractionSize)) {
16793
      fractionSize = formats.PATTERNS[1].maxFrac;
16794
    }
16795
 
16796
    // if null or undefined pass it through
16797
    return (amount == null)
16798
        ? amount
16799
        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
16800
            replace(/\u00A4/g, currencySymbol);
16801
  };
16802
}
16803
 
16804
/**
16805
 * @ngdoc filter
16806
 * @name number
16807
 * @kind function
16808
 *
16809
 * @description
16810
 * Formats a number as text.
16811
 *
16812
 * If the input is not a number an empty string is returned.
16813
 *
16814
 * @param {number|string} number Number to format.
16815
 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
16816
 * If this is not provided then the fraction size is computed from the current locale's number
16817
 * formatting pattern. In the case of the default locale, it will be 3.
16818
 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
16819
 *
16820
 * @example
16821
   <example module="numberFilterExample">
16822
     <file name="index.html">
16823
       <script>
16824
         angular.module('numberFilterExample', [])
16825
           .controller('ExampleController', ['$scope', function($scope) {
16826
             $scope.val = 1234.56789;
16827
           }]);
16828
       </script>
16829
       <div ng-controller="ExampleController">
16830
         Enter number: <input ng-model='val'><br>
16831
         Default formatting: <span id='number-default'>{{val | number}}</span><br>
16832
         No fractions: <span>{{val | number:0}}</span><br>
16833
         Negative number: <span>{{-val | number:4}}</span>
16834
       </div>
16835
     </file>
16836
     <file name="protractor.js" type="protractor">
16837
       it('should format numbers', function() {
16838
         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
16839
         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
16840
         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
16841
       });
16842
 
16843
       it('should update', function() {
16844
         element(by.model('val')).clear();
16845
         element(by.model('val')).sendKeys('3374.333');
16846
         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
16847
         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
16848
         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
16849
      });
16850
     </file>
16851
   </example>
16852
 */
16853
 
16854
 
16855
numberFilter.$inject = ['$locale'];
16856
function numberFilter($locale) {
16857
  var formats = $locale.NUMBER_FORMATS;
16858
  return function(number, fractionSize) {
16859
 
16860
    // if null or undefined pass it through
16861
    return (number == null)
16862
        ? number
16863
        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
16864
                       fractionSize);
16865
  };
16866
}
16867
 
16868
var DECIMAL_SEP = '.';
16869
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16870
  if (!isFinite(number) || isObject(number)) return '';
16871
 
16872
  var isNegative = number < 0;
16873
  number = Math.abs(number);
16874
  var numStr = number + '',
16875
      formatedText = '',
16876
      parts = [];
16877
 
16878
  var hasExponent = false;
16879
  if (numStr.indexOf('e') !== -1) {
16880
    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
16881
    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
16882
      number = 0;
16883
    } else {
16884
      formatedText = numStr;
16885
      hasExponent = true;
16886
    }
16887
  }
16888
 
16889
  if (!hasExponent) {
16890
    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
16891
 
16892
    // determine fractionSize if it is not specified
16893
    if (isUndefined(fractionSize)) {
16894
      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
16895
    }
16896
 
16897
    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
16898
    // inspired by:
16899
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
16900
    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
16901
 
16902
    var fraction = ('' + number).split(DECIMAL_SEP);
16903
    var whole = fraction[0];
16904
    fraction = fraction[1] || '';
16905
 
16906
    var i, pos = 0,
16907
        lgroup = pattern.lgSize,
16908
        group = pattern.gSize;
16909
 
16910
    if (whole.length >= (lgroup + group)) {
16911
      pos = whole.length - lgroup;
16912
      for (i = 0; i < pos; i++) {
16913
        if ((pos - i) % group === 0 && i !== 0) {
16914
          formatedText += groupSep;
16915
        }
16916
        formatedText += whole.charAt(i);
16917
      }
16918
    }
16919
 
16920
    for (i = pos; i < whole.length; i++) {
16921
      if ((whole.length - i) % lgroup === 0 && i !== 0) {
16922
        formatedText += groupSep;
16923
      }
16924
      formatedText += whole.charAt(i);
16925
    }
16926
 
16927
    // format fraction part.
16928
    while (fraction.length < fractionSize) {
16929
      fraction += '0';
16930
    }
16931
 
16932
    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
16933
  } else {
16934
    if (fractionSize > 0 && number < 1) {
16935
      formatedText = number.toFixed(fractionSize);
16936
      number = parseFloat(formatedText);
16937
    }
16938
  }
16939
 
16940
  if (number === 0) {
16941
    isNegative = false;
16942
  }
16943
 
16944
  parts.push(isNegative ? pattern.negPre : pattern.posPre,
16945
             formatedText,
16946
             isNegative ? pattern.negSuf : pattern.posSuf);
16947
  return parts.join('');
16948
}
16949
 
16950
function padNumber(num, digits, trim) {
16951
  var neg = '';
16952
  if (num < 0) {
16953
    neg =  '-';
16954
    num = -num;
16955
  }
16956
  num = '' + num;
16957
  while (num.length < digits) num = '0' + num;
16958
  if (trim)
16959
    num = num.substr(num.length - digits);
16960
  return neg + num;
16961
}
16962
 
16963
 
16964
function dateGetter(name, size, offset, trim) {
16965
  offset = offset || 0;
16966
  return function(date) {
16967
    var value = date['get' + name]();
16968
    if (offset > 0 || value > -offset)
16969
      value += offset;
16970
    if (value === 0 && offset == -12) value = 12;
16971
    return padNumber(value, size, trim);
16972
  };
16973
}
16974
 
16975
function dateStrGetter(name, shortForm) {
16976
  return function(date, formats) {
16977
    var value = date['get' + name]();
16978
    var get = uppercase(shortForm ? ('SHORT' + name) : name);
16979
 
16980
    return formats[get][value];
16981
  };
16982
}
16983
 
16984
function timeZoneGetter(date) {
16985
  var zone = -1 * date.getTimezoneOffset();
16986
  var paddedZone = (zone >= 0) ? "+" : "";
16987
 
16988
  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
16989
                padNumber(Math.abs(zone % 60), 2);
16990
 
16991
  return paddedZone;
16992
}
16993
 
16994
function getFirstThursdayOfYear(year) {
16995
    // 0 = index of January
16996
    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
16997
    // 4 = index of Thursday (+1 to account for 1st = 5)
16998
    // 11 = index of *next* Thursday (+1 account for 1st = 12)
16999
    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
17000
}
17001
 
17002
function getThursdayThisWeek(datetime) {
17003
    return new Date(datetime.getFullYear(), datetime.getMonth(),
17004
      // 4 = index of Thursday
17005
      datetime.getDate() + (4 - datetime.getDay()));
17006
}
17007
 
17008
function weekGetter(size) {
17009
   return function(date) {
17010
      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
17011
         thisThurs = getThursdayThisWeek(date);
17012
 
17013
      var diff = +thisThurs - +firstThurs,
17014
         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
17015
 
17016
      return padNumber(result, size);
17017
   };
17018
}
17019
 
17020
function ampmGetter(date, formats) {
17021
  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
17022
}
17023
 
17024
var DATE_FORMATS = {
17025
  yyyy: dateGetter('FullYear', 4),
17026
    yy: dateGetter('FullYear', 2, 0, true),
17027
     y: dateGetter('FullYear', 1),
17028
  MMMM: dateStrGetter('Month'),
17029
   MMM: dateStrGetter('Month', true),
17030
    MM: dateGetter('Month', 2, 1),
17031
     M: dateGetter('Month', 1, 1),
17032
    dd: dateGetter('Date', 2),
17033
     d: dateGetter('Date', 1),
17034
    HH: dateGetter('Hours', 2),
17035
     H: dateGetter('Hours', 1),
17036
    hh: dateGetter('Hours', 2, -12),
17037
     h: dateGetter('Hours', 1, -12),
17038
    mm: dateGetter('Minutes', 2),
17039
     m: dateGetter('Minutes', 1),
17040
    ss: dateGetter('Seconds', 2),
17041
     s: dateGetter('Seconds', 1),
17042
     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
17043
     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
17044
   sss: dateGetter('Milliseconds', 3),
17045
  EEEE: dateStrGetter('Day'),
17046
   EEE: dateStrGetter('Day', true),
17047
     a: ampmGetter,
17048
     Z: timeZoneGetter,
17049
    ww: weekGetter(2),
17050
     w: weekGetter(1)
17051
};
17052
 
17053
var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
17054
    NUMBER_STRING = /^\-?\d+$/;
17055
 
17056
/**
17057
 * @ngdoc filter
17058
 * @name date
17059
 * @kind function
17060
 *
17061
 * @description
17062
 *   Formats `date` to a string based on the requested `format`.
17063
 *
17064
 *   `format` string can be composed of the following elements:
17065
 *
17066
 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
17067
 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
17068
 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
17069
 *   * `'MMMM'`: Month in year (January-December)
17070
 *   * `'MMM'`: Month in year (Jan-Dec)
17071
 *   * `'MM'`: Month in year, padded (01-12)
17072
 *   * `'M'`: Month in year (1-12)
17073
 *   * `'dd'`: Day in month, padded (01-31)
17074
 *   * `'d'`: Day in month (1-31)
17075
 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
17076
 *   * `'EEE'`: Day in Week, (Sun-Sat)
17077
 *   * `'HH'`: Hour in day, padded (00-23)
17078
 *   * `'H'`: Hour in day (0-23)
17079
 *   * `'hh'`: Hour in AM/PM, padded (01-12)
17080
 *   * `'h'`: Hour in AM/PM, (1-12)
17081
 *   * `'mm'`: Minute in hour, padded (00-59)
17082
 *   * `'m'`: Minute in hour (0-59)
17083
 *   * `'ss'`: Second in minute, padded (00-59)
17084
 *   * `'s'`: Second in minute (0-59)
17085
 *   * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
17086
 *   * `'a'`: AM/PM marker
17087
 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
17088
 *   * `'ww'`: ISO-8601 week of year (00-53)
17089
 *   * `'w'`: ISO-8601 week of year (0-53)
17090
 *
17091
 *   `format` string can also be one of the following predefined
17092
 *   {@link guide/i18n localizable formats}:
17093
 *
17094
 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
17095
 *     (e.g. Sep 3, 2010 12:05:08 PM)
17096
 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
17097
 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
17098
 *     (e.g. Friday, September 3, 2010)
17099
 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
17100
 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
17101
 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
17102
 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
17103
 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
17104
 *
17105
 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
17106
 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
17107
 *   (e.g. `"h 'o''clock'"`).
17108
 *
17109
 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
17110
 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
17111
 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
17112
 *    specified in the string input, the time is considered to be in the local timezone.
17113
 * @param {string=} format Formatting rules (see Description). If not specified,
17114
 *    `mediumDate` is used.
17115
 * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.
17116
 *    If not specified, the timezone of the browser will be used.
17117
 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
17118
 *
17119
 * @example
17120
   <example>
17121
     <file name="index.html">
17122
       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
17123
           <span>{{1288323623006 | date:'medium'}}</span><br>
17124
       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
17125
          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
17126
       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
17127
          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
17128
       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
17129
          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
17130
     </file>
17131
     <file name="protractor.js" type="protractor">
17132
       it('should format date', function() {
17133
         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
17134
            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
17135
         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
17136
            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
17137
         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
17138
            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
17139
         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
17140
            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
17141
       });
17142
     </file>
17143
   </example>
17144
 */
17145
dateFilter.$inject = ['$locale'];
17146
function dateFilter($locale) {
17147
 
17148
 
17149
  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
17150
                     // 1        2       3         4          5          6          7          8  9     10      11
17151
  function jsonStringToDate(string) {
17152
    var match;
17153
    if (match = string.match(R_ISO8601_STR)) {
17154
      var date = new Date(0),
17155
          tzHour = 0,
17156
          tzMin  = 0,
17157
          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
17158
          timeSetter = match[8] ? date.setUTCHours : date.setHours;
17159
 
17160
      if (match[9]) {
17161
        tzHour = int(match[9] + match[10]);
17162
        tzMin = int(match[9] + match[11]);
17163
      }
17164
      dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17165
      var h = int(match[4] || 0) - tzHour;
17166
      var m = int(match[5] || 0) - tzMin;
17167
      var s = int(match[6] || 0);
17168
      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17169
      timeSetter.call(date, h, m, s, ms);
17170
      return date;
17171
    }
17172
    return string;
17173
  }
17174
 
17175
 
17176
  return function(date, format, timezone) {
17177
    var text = '',
17178
        parts = [],
17179
        fn, match;
17180
 
17181
    format = format || 'mediumDate';
17182
    format = $locale.DATETIME_FORMATS[format] || format;
17183
    if (isString(date)) {
17184
      date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
17185
    }
17186
 
17187
    if (isNumber(date)) {
17188
      date = new Date(date);
17189
    }
17190
 
17191
    if (!isDate(date)) {
17192
      return date;
17193
    }
17194
 
17195
    while (format) {
17196
      match = DATE_FORMATS_SPLIT.exec(format);
17197
      if (match) {
17198
        parts = concat(parts, match, 1);
17199
        format = parts.pop();
17200
      } else {
17201
        parts.push(format);
17202
        format = null;
17203
      }
17204
    }
17205
 
17206
    if (timezone && timezone === 'UTC') {
17207
      date = new Date(date.getTime());
17208
      date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
17209
    }
17210
    forEach(parts, function(value) {
17211
      fn = DATE_FORMATS[value];
17212
      text += fn ? fn(date, $locale.DATETIME_FORMATS)
17213
                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
17214
    });
17215
 
17216
    return text;
17217
  };
17218
}
17219
 
17220
 
17221
/**
17222
 * @ngdoc filter
17223
 * @name json
17224
 * @kind function
17225
 *
17226
 * @description
17227
 *   Allows you to convert a JavaScript object into JSON string.
17228
 *
17229
 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
17230
 *   the binding is automatically converted to JSON.
17231
 *
17232
 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
17233
 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
17234
 * @returns {string} JSON string.
17235
 *
17236
 *
17237
 * @example
17238
   <example>
17239
     <file name="index.html">
17240
       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
17241
       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
17242
     </file>
17243
     <file name="protractor.js" type="protractor">
17244
       it('should jsonify filtered objects', function() {
17245
         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
17246
         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
17247
       });
17248
     </file>
17249
   </example>
17250
 *
17251
 */
17252
function jsonFilter() {
17253
  return function(object, spacing) {
17254
    if (isUndefined(spacing)) {
17255
        spacing = 2;
17256
    }
17257
    return toJson(object, spacing);
17258
  };
17259
}
17260
 
17261
 
17262
/**
17263
 * @ngdoc filter
17264
 * @name lowercase
17265
 * @kind function
17266
 * @description
17267
 * Converts string to lowercase.
17268
 * @see angular.lowercase
17269
 */
17270
var lowercaseFilter = valueFn(lowercase);
17271
 
17272
 
17273
/**
17274
 * @ngdoc filter
17275
 * @name uppercase
17276
 * @kind function
17277
 * @description
17278
 * Converts string to uppercase.
17279
 * @see angular.uppercase
17280
 */
17281
var uppercaseFilter = valueFn(uppercase);
17282
 
17283
/**
17284
 * @ngdoc filter
17285
 * @name limitTo
17286
 * @kind function
17287
 *
17288
 * @description
17289
 * Creates a new array or string containing only a specified number of elements. The elements
17290
 * are taken from either the beginning or the end of the source array, string or number, as specified by
17291
 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
17292
 * converted to a string.
17293
 *
17294
 * @param {Array|string|number} input Source array, string or number to be limited.
17295
 * @param {string|number} limit The length of the returned array or string. If the `limit` number
17296
 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
17297
 *     If the number is negative, `limit` number  of items from the end of the source array/string
17298
 *     are copied. The `limit` will be trimmed if it exceeds `array.length`
17299
 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
17300
 *     had less than `limit` elements.
17301
 *
17302
 * @example
17303
   <example module="limitToExample">
17304
     <file name="index.html">
17305
       <script>
17306
         angular.module('limitToExample', [])
17307
           .controller('ExampleController', ['$scope', function($scope) {
17308
             $scope.numbers = [1,2,3,4,5,6,7,8,9];
17309
             $scope.letters = "abcdefghi";
17310
             $scope.longNumber = 2345432342;
17311
             $scope.numLimit = 3;
17312
             $scope.letterLimit = 3;
17313
             $scope.longNumberLimit = 3;
17314
           }]);
17315
       </script>
17316
       <div ng-controller="ExampleController">
17317
         Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
17318
         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
17319
         Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
17320
         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
17321
         Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit">
17322
         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
17323
       </div>
17324
     </file>
17325
     <file name="protractor.js" type="protractor">
17326
       var numLimitInput = element(by.model('numLimit'));
17327
       var letterLimitInput = element(by.model('letterLimit'));
17328
       var longNumberLimitInput = element(by.model('longNumberLimit'));
17329
       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
17330
       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
17331
       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
17332
 
17333
       it('should limit the number array to first three items', function() {
17334
         expect(numLimitInput.getAttribute('value')).toBe('3');
17335
         expect(letterLimitInput.getAttribute('value')).toBe('3');
17336
         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
17337
         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
17338
         expect(limitedLetters.getText()).toEqual('Output letters: abc');
17339
         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
17340
       });
17341
 
17342
       // There is a bug in safari and protractor that doesn't like the minus key
17343
       // it('should update the output when -3 is entered', function() {
17344
       //   numLimitInput.clear();
17345
       //   numLimitInput.sendKeys('-3');
17346
       //   letterLimitInput.clear();
17347
       //   letterLimitInput.sendKeys('-3');
17348
       //   longNumberLimitInput.clear();
17349
       //   longNumberLimitInput.sendKeys('-3');
17350
       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
17351
       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
17352
       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
17353
       // });
17354
 
17355
       it('should not exceed the maximum size of input array', function() {
17356
         numLimitInput.clear();
17357
         numLimitInput.sendKeys('100');
17358
         letterLimitInput.clear();
17359
         letterLimitInput.sendKeys('100');
17360
         longNumberLimitInput.clear();
17361
         longNumberLimitInput.sendKeys('100');
17362
         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
17363
         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
17364
         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
17365
       });
17366
     </file>
17367
   </example>
17368
*/
17369
function limitToFilter() {
17370
  return function(input, limit) {
17371
    if (isNumber(input)) input = input.toString();
17372
    if (!isArray(input) && !isString(input)) return input;
17373
 
17374
    if (Math.abs(Number(limit)) === Infinity) {
17375
      limit = Number(limit);
17376
    } else {
17377
      limit = int(limit);
17378
    }
17379
 
17380
    if (isString(input)) {
17381
      //NaN check on limit
17382
      if (limit) {
17383
        return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
17384
      } else {
17385
        return "";
17386
      }
17387
    }
17388
 
17389
    var out = [],
17390
      i, n;
17391
 
17392
    // if abs(limit) exceeds maximum length, trim it
17393
    if (limit > input.length)
17394
      limit = input.length;
17395
    else if (limit < -input.length)
17396
      limit = -input.length;
17397
 
17398
    if (limit > 0) {
17399
      i = 0;
17400
      n = limit;
17401
    } else {
17402
      i = input.length + limit;
17403
      n = input.length;
17404
    }
17405
 
17406
    for (; i < n; i++) {
17407
      out.push(input[i]);
17408
    }
17409
 
17410
    return out;
17411
  };
17412
}
17413
 
17414
/**
17415
 * @ngdoc filter
17416
 * @name orderBy
17417
 * @kind function
17418
 *
17419
 * @description
17420
 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
17421
 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
17422
 * correctly, make sure they are actually being saved as numbers and not strings.
17423
 *
17424
 * @param {Array} array The array to sort.
17425
 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
17426
 *    used by the comparator to determine the order of elements.
17427
 *
17428
 *    Can be one of:
17429
 *
17430
 *    - `function`: Getter function. The result of this function will be sorted using the
17431
 *      `<`, `=`, `>` operator.
17432
 *    - `string`: An Angular expression. The result of this expression is used to compare elements
17433
 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
17434
 *      3 first characters of a property called `name`). The result of a constant expression
17435
 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
17436
 *      to sort object by the value of their `special name` property). An expression can be
17437
 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
17438
 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
17439
 *      element itself is used to compare where sorting.
17440
 *    - `Array`: An array of function or string predicates. The first predicate in the array
17441
 *      is used for sorting, but when two items are equivalent, the next predicate is used.
17442
 *
17443
 *    If the predicate is missing or empty then it defaults to `'+'`.
17444
 *
17445
 * @param {boolean=} reverse Reverse the order of the array.
17446
 * @returns {Array} Sorted copy of the source array.
17447
 *
17448
 * @example
17449
   <example module="orderByExample">
17450
     <file name="index.html">
17451
       <script>
17452
         angular.module('orderByExample', [])
17453
           .controller('ExampleController', ['$scope', function($scope) {
17454
             $scope.friends =
17455
                 [{name:'John', phone:'555-1212', age:10},
17456
                  {name:'Mary', phone:'555-9876', age:19},
17457
                  {name:'Mike', phone:'555-4321', age:21},
17458
                  {name:'Adam', phone:'555-5678', age:35},
17459
                  {name:'Julie', phone:'555-8765', age:29}];
17460
             $scope.predicate = '-age';
17461
           }]);
17462
       </script>
17463
       <div ng-controller="ExampleController">
17464
         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
17465
         <hr/>
17466
         [ <a href="" ng-click="predicate=''">unsorted</a> ]
17467
         <table class="friend">
17468
           <tr>
17469
             <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
17470
                 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
17471
             <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
17472
             <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
17473
           </tr>
17474
           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
17475
             <td>{{friend.name}}</td>
17476
             <td>{{friend.phone}}</td>
17477
             <td>{{friend.age}}</td>
17478
           </tr>
17479
         </table>
17480
       </div>
17481
     </file>
17482
   </example>
17483
 *
17484
 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
17485
 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
17486
 * desired parameters.
17487
 *
17488
 * Example:
17489
 *
17490
 * @example
17491
  <example module="orderByExample">
17492
    <file name="index.html">
17493
      <div ng-controller="ExampleController">
17494
        <table class="friend">
17495
          <tr>
17496
            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
17497
              (<a href="" ng-click="order('-name',false)">^</a>)</th>
17498
            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
17499
            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
17500
          </tr>
17501
          <tr ng-repeat="friend in friends">
17502
            <td>{{friend.name}}</td>
17503
            <td>{{friend.phone}}</td>
17504
            <td>{{friend.age}}</td>
17505
          </tr>
17506
        </table>
17507
      </div>
17508
    </file>
17509
 
17510
    <file name="script.js">
17511
      angular.module('orderByExample', [])
17512
        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
17513
          var orderBy = $filter('orderBy');
17514
          $scope.friends = [
17515
            { name: 'John',    phone: '555-1212',    age: 10 },
17516
            { name: 'Mary',    phone: '555-9876',    age: 19 },
17517
            { name: 'Mike',    phone: '555-4321',    age: 21 },
17518
            { name: 'Adam',    phone: '555-5678',    age: 35 },
17519
            { name: 'Julie',   phone: '555-8765',    age: 29 }
17520
          ];
17521
          $scope.order = function(predicate, reverse) {
17522
            $scope.friends = orderBy($scope.friends, predicate, reverse);
17523
          };
17524
          $scope.order('-age',false);
17525
        }]);
17526
    </file>
17527
</example>
17528
 */
17529
orderByFilter.$inject = ['$parse'];
17530
function orderByFilter($parse) {
17531
  return function(array, sortPredicate, reverseOrder) {
17532
    if (!(isArrayLike(array))) return array;
17533
    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17534
    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17535
    sortPredicate = sortPredicate.map(function(predicate) {
17536
      var descending = false, get = predicate || identity;
17537
      if (isString(predicate)) {
17538
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
17539
          descending = predicate.charAt(0) == '-';
17540
          predicate = predicate.substring(1);
17541
        }
17542
        if (predicate === '') {
17543
          // Effectively no predicate was passed so we compare identity
17544
          return reverseComparator(function(a, b) {
17545
            return compare(a, b);
17546
          }, descending);
17547
        }
17548
        get = $parse(predicate);
17549
        if (get.constant) {
17550
          var key = get();
17551
          return reverseComparator(function(a, b) {
17552
            return compare(a[key], b[key]);
17553
          }, descending);
17554
        }
17555
      }
17556
      return reverseComparator(function(a, b) {
17557
        return compare(get(a),get(b));
17558
      }, descending);
17559
    });
17560
    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17561
 
17562
    function comparator(o1, o2) {
17563
      for (var i = 0; i < sortPredicate.length; i++) {
17564
        var comp = sortPredicate[i](o1, o2);
17565
        if (comp !== 0) return comp;
17566
      }
17567
      return 0;
17568
    }
17569
    function reverseComparator(comp, descending) {
17570
      return descending
17571
          ? function(a, b) {return comp(b,a);}
17572
          : comp;
17573
    }
17574
    function compare(v1, v2) {
17575
      var t1 = typeof v1;
17576
      var t2 = typeof v2;
17577
      // Prepare values for Abstract Relational Comparison
17578
      // (http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5):
17579
      // If the resulting values are identical, return 0 to prevent
17580
      // incorrect re-ordering.
17581
      if (t1 === t2 && t1 === "object") {
17582
        // If types are both numbers, emulate abstract ToPrimitive() operation
17583
        // in order to get primitive values suitable for comparison
17584
        t1 = typeof (v1.valueOf ? v1 = v1.valueOf() : v1);
17585
        t2 = typeof (v2.valueOf ? v2 = v2.valueOf() : v2);
17586
        if (t1 === t2 && t1 === "object") {
17587
          // Object.prototype.valueOf will return the original object, by
17588
          // default. If we do not receive a primitive value, use ToString()
17589
          // instead.
17590
          t1 = typeof (v1.toString ? v1 = v1.toString() : v1);
17591
          t2 = typeof (v2.toString ? v2 = v2.toString() : v2);
17592
 
17593
          // If the end result of toString() for each item is the same, do not
17594
          // perform relational comparison, and do not re-order objects.
17595
          if (t1 === t2 && v1 === v2 || t1 === "object") return 0;
17596
        }
17597
      }
17598
      if (t1 === t2) {
17599
        if (t1 === "string") {
17600
           v1 = v1.toLowerCase();
17601
           v2 = v2.toLowerCase();
17602
        }
17603
        if (v1 === v2) return 0;
17604
        return v1 < v2 ? -1 : 1;
17605
      } else {
17606
        return t1 < t2 ? -1 : 1;
17607
      }
17608
    }
17609
  };
17610
}
17611
 
17612
function ngDirective(directive) {
17613
  if (isFunction(directive)) {
17614
    directive = {
17615
      link: directive
17616
    };
17617
  }
17618
  directive.restrict = directive.restrict || 'AC';
17619
  return valueFn(directive);
17620
}
17621
 
17622
/**
17623
 * @ngdoc directive
17624
 * @name a
17625
 * @restrict E
17626
 *
17627
 * @description
17628
 * Modifies the default behavior of the html A tag so that the default action is prevented when
17629
 * the href attribute is empty.
17630
 *
17631
 * This change permits the easy creation of action links with the `ngClick` directive
17632
 * without changing the location or causing page reloads, e.g.:
17633
 * `<a href="" ng-click="list.addItem()">Add Item</a>`
17634
 */
17635
var htmlAnchorDirective = valueFn({
17636
  restrict: 'E',
17637
  compile: function(element, attr) {
17638
    if (!attr.href && !attr.xlinkHref && !attr.name) {
17639
      return function(scope, element) {
17640
        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
17641
        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
17642
                   'xlink:href' : 'href';
17643
        element.on('click', function(event) {
17644
          // if we have no href url, then don't navigate anywhere.
17645
          if (!element.attr(href)) {
17646
            event.preventDefault();
17647
          }
17648
        });
17649
      };
17650
    }
17651
  }
17652
});
17653
 
17654
/**
17655
 * @ngdoc directive
17656
 * @name ngHref
17657
 * @restrict A
17658
 * @priority 99
17659
 *
17660
 * @description
17661
 * Using Angular markup like `{{hash}}` in an href attribute will
17662
 * make the link go to the wrong URL if the user clicks it before
17663
 * Angular has a chance to replace the `{{hash}}` markup with its
17664
 * value. Until Angular replaces the markup the link will be broken
17665
 * and will most likely return a 404 error. The `ngHref` directive
17666
 * solves this problem.
17667
 *
17668
 * The wrong way to write it:
17669
 * ```html
17670
 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17671
 * ```
17672
 *
17673
 * The correct way to write it:
17674
 * ```html
17675
 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17676
 * ```
17677
 *
17678
 * @element A
17679
 * @param {template} ngHref any string which can contain `{{}}` markup.
17680
 *
17681
 * @example
17682
 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
17683
 * in links and their different behaviors:
17684
    <example>
17685
      <file name="index.html">
17686
        <input ng-model="value" /><br />
17687
        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
17688
        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
17689
        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
17690
        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
17691
        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
17692
        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
17693
      </file>
17694
      <file name="protractor.js" type="protractor">
17695
        it('should execute ng-click but not reload when href without value', function() {
17696
          element(by.id('link-1')).click();
17697
          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
17698
          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
17699
        });
17700
 
17701
        it('should execute ng-click but not reload when href empty string', function() {
17702
          element(by.id('link-2')).click();
17703
          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
17704
          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
17705
        });
17706
 
17707
        it('should execute ng-click and change url when ng-href specified', function() {
17708
          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
17709
 
17710
          element(by.id('link-3')).click();
17711
 
17712
          // At this point, we navigate away from an Angular page, so we need
17713
          // to use browser.driver to get the base webdriver.
17714
 
17715
          browser.wait(function() {
17716
            return browser.driver.getCurrentUrl().then(function(url) {
17717
              return url.match(/\/123$/);
17718
            });
17719
          }, 5000, 'page should navigate to /123');
17720
        });
17721
 
17722
        xit('should execute ng-click but not reload when href empty string and name specified', function() {
17723
          element(by.id('link-4')).click();
17724
          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
17725
          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
17726
        });
17727
 
17728
        it('should execute ng-click but not reload when no href but name specified', function() {
17729
          element(by.id('link-5')).click();
17730
          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
17731
          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
17732
        });
17733
 
17734
        it('should only change url when only ng-href', function() {
17735
          element(by.model('value')).clear();
17736
          element(by.model('value')).sendKeys('6');
17737
          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
17738
 
17739
          element(by.id('link-6')).click();
17740
 
17741
          // At this point, we navigate away from an Angular page, so we need
17742
          // to use browser.driver to get the base webdriver.
17743
          browser.wait(function() {
17744
            return browser.driver.getCurrentUrl().then(function(url) {
17745
              return url.match(/\/6$/);
17746
            });
17747
          }, 5000, 'page should navigate to /6');
17748
        });
17749
      </file>
17750
    </example>
17751
 */
17752
 
17753
/**
17754
 * @ngdoc directive
17755
 * @name ngSrc
17756
 * @restrict A
17757
 * @priority 99
17758
 *
17759
 * @description
17760
 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
17761
 * work right: The browser will fetch from the URL with the literal
17762
 * text `{{hash}}` until Angular replaces the expression inside
17763
 * `{{hash}}`. The `ngSrc` directive solves this problem.
17764
 *
17765
 * The buggy way to write it:
17766
 * ```html
17767
 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
17768
 * ```
17769
 *
17770
 * The correct way to write it:
17771
 * ```html
17772
 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
17773
 * ```
17774
 *
17775
 * @element IMG
17776
 * @param {template} ngSrc any string which can contain `{{}}` markup.
17777
 */
17778
 
17779
/**
17780
 * @ngdoc directive
17781
 * @name ngSrcset
17782
 * @restrict A
17783
 * @priority 99
17784
 *
17785
 * @description
17786
 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
17787
 * work right: The browser will fetch from the URL with the literal
17788
 * text `{{hash}}` until Angular replaces the expression inside
17789
 * `{{hash}}`. The `ngSrcset` directive solves this problem.
17790
 *
17791
 * The buggy way to write it:
17792
 * ```html
17793
 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17794
 * ```
17795
 *
17796
 * The correct way to write it:
17797
 * ```html
17798
 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17799
 * ```
17800
 *
17801
 * @element IMG
17802
 * @param {template} ngSrcset any string which can contain `{{}}` markup.
17803
 */
17804
 
17805
/**
17806
 * @ngdoc directive
17807
 * @name ngDisabled
17808
 * @restrict A
17809
 * @priority 100
17810
 *
17811
 * @description
17812
 *
17813
 * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
17814
 * ```html
17815
 * <div ng-init="scope = { isDisabled: false }">
17816
 *  <button disabled="{{scope.isDisabled}}">Disabled</button>
17817
 * </div>
17818
 * ```
17819
 *
17820
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17821
 * such as disabled. (Their presence means true and their absence means false.)
17822
 * If we put an Angular interpolation expression into such an attribute then the
17823
 * binding information would be lost when the browser removes the attribute.
17824
 * The `ngDisabled` directive solves this problem for the `disabled` attribute.
17825
 * This complementary directive is not removed by the browser and so provides
17826
 * a permanent reliable place to store the binding information.
17827
 *
17828
 * @example
17829
    <example>
17830
      <file name="index.html">
17831
        Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
17832
        <button ng-model="button" ng-disabled="checked">Button</button>
17833
      </file>
17834
      <file name="protractor.js" type="protractor">
17835
        it('should toggle button', function() {
17836
          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
17837
          element(by.model('checked')).click();
17838
          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
17839
        });
17840
      </file>
17841
    </example>
17842
 *
17843
 * @element INPUT
17844
 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
17845
 *     then special attribute "disabled" will be set on the element
17846
 */
17847
 
17848
 
17849
/**
17850
 * @ngdoc directive
17851
 * @name ngChecked
17852
 * @restrict A
17853
 * @priority 100
17854
 *
17855
 * @description
17856
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17857
 * such as checked. (Their presence means true and their absence means false.)
17858
 * If we put an Angular interpolation expression into such an attribute then the
17859
 * binding information would be lost when the browser removes the attribute.
17860
 * The `ngChecked` directive solves this problem for the `checked` attribute.
17861
 * This complementary directive is not removed by the browser and so provides
17862
 * a permanent reliable place to store the binding information.
17863
 * @example
17864
    <example>
17865
      <file name="index.html">
17866
        Check me to check both: <input type="checkbox" ng-model="master"><br/>
17867
        <input id="checkSlave" type="checkbox" ng-checked="master">
17868
      </file>
17869
      <file name="protractor.js" type="protractor">
17870
        it('should check both checkBoxes', function() {
17871
          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
17872
          element(by.model('master')).click();
17873
          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
17874
        });
17875
      </file>
17876
    </example>
17877
 *
17878
 * @element INPUT
17879
 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
17880
 *     then special attribute "checked" will be set on the element
17881
 */
17882
 
17883
 
17884
/**
17885
 * @ngdoc directive
17886
 * @name ngReadonly
17887
 * @restrict A
17888
 * @priority 100
17889
 *
17890
 * @description
17891
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17892
 * such as readonly. (Their presence means true and their absence means false.)
17893
 * If we put an Angular interpolation expression into such an attribute then the
17894
 * binding information would be lost when the browser removes the attribute.
17895
 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
17896
 * This complementary directive is not removed by the browser and so provides
17897
 * a permanent reliable place to store the binding information.
17898
 * @example
17899
    <example>
17900
      <file name="index.html">
17901
        Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
17902
        <input type="text" ng-readonly="checked" value="I'm Angular"/>
17903
      </file>
17904
      <file name="protractor.js" type="protractor">
17905
        it('should toggle readonly attr', function() {
17906
          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
17907
          element(by.model('checked')).click();
17908
          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
17909
        });
17910
      </file>
17911
    </example>
17912
 *
17913
 * @element INPUT
17914
 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
17915
 *     then special attribute "readonly" will be set on the element
17916
 */
17917
 
17918
 
17919
/**
17920
 * @ngdoc directive
17921
 * @name ngSelected
17922
 * @restrict A
17923
 * @priority 100
17924
 *
17925
 * @description
17926
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17927
 * such as selected. (Their presence means true and their absence means false.)
17928
 * If we put an Angular interpolation expression into such an attribute then the
17929
 * binding information would be lost when the browser removes the attribute.
17930
 * The `ngSelected` directive solves this problem for the `selected` attribute.
17931
 * This complementary directive is not removed by the browser and so provides
17932
 * a permanent reliable place to store the binding information.
17933
 *
17934
 * @example
17935
    <example>
17936
      <file name="index.html">
17937
        Check me to select: <input type="checkbox" ng-model="selected"><br/>
17938
        <select>
17939
          <option>Hello!</option>
17940
          <option id="greet" ng-selected="selected">Greetings!</option>
17941
        </select>
17942
      </file>
17943
      <file name="protractor.js" type="protractor">
17944
        it('should select Greetings!', function() {
17945
          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
17946
          element(by.model('selected')).click();
17947
          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
17948
        });
17949
      </file>
17950
    </example>
17951
 *
17952
 * @element OPTION
17953
 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
17954
 *     then special attribute "selected" will be set on the element
17955
 */
17956
 
17957
/**
17958
 * @ngdoc directive
17959
 * @name ngOpen
17960
 * @restrict A
17961
 * @priority 100
17962
 *
17963
 * @description
17964
 * The HTML specification does not require browsers to preserve the values of boolean attributes
17965
 * such as open. (Their presence means true and their absence means false.)
17966
 * If we put an Angular interpolation expression into such an attribute then the
17967
 * binding information would be lost when the browser removes the attribute.
17968
 * The `ngOpen` directive solves this problem for the `open` attribute.
17969
 * This complementary directive is not removed by the browser and so provides
17970
 * a permanent reliable place to store the binding information.
17971
 * @example
17972
     <example>
17973
       <file name="index.html">
17974
         Check me check multiple: <input type="checkbox" ng-model="open"><br/>
17975
         <details id="details" ng-open="open">
17976
            <summary>Show/Hide me</summary>
17977
         </details>
17978
       </file>
17979
       <file name="protractor.js" type="protractor">
17980
         it('should toggle open', function() {
17981
           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
17982
           element(by.model('open')).click();
17983
           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
17984
         });
17985
       </file>
17986
     </example>
17987
 *
17988
 * @element DETAILS
17989
 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
17990
 *     then special attribute "open" will be set on the element
17991
 */
17992
 
17993
var ngAttributeAliasDirectives = {};
17994
 
17995
 
17996
// boolean attrs are evaluated
17997
forEach(BOOLEAN_ATTR, function(propName, attrName) {
17998
  // binding to multiple is not supported
17999
  if (propName == "multiple") return;
18000
 
18001
  var normalized = directiveNormalize('ng-' + attrName);
18002
  ngAttributeAliasDirectives[normalized] = function() {
18003
    return {
18004
      restrict: 'A',
18005
      priority: 100,
18006
      link: function(scope, element, attr) {
18007
        scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
18008
          attr.$set(attrName, !!value);
18009
        });
18010
      }
18011
    };
18012
  };
18013
});
18014
 
18015
// aliased input attrs are evaluated
18016
forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
18017
  ngAttributeAliasDirectives[ngAttr] = function() {
18018
    return {
18019
      priority: 100,
18020
      link: function(scope, element, attr) {
18021
        //special case ngPattern when a literal regular expression value
18022
        //is used as the expression (this way we don't have to watch anything).
18023
        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
18024
          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
18025
          if (match) {
18026
            attr.$set("ngPattern", new RegExp(match[1], match[2]));
18027
            return;
18028
          }
18029
        }
18030
 
18031
        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
18032
          attr.$set(ngAttr, value);
18033
        });
18034
      }
18035
    };
18036
  };
18037
});
18038
 
18039
// ng-src, ng-srcset, ng-href are interpolated
18040
forEach(['src', 'srcset', 'href'], function(attrName) {
18041
  var normalized = directiveNormalize('ng-' + attrName);
18042
  ngAttributeAliasDirectives[normalized] = function() {
18043
    return {
18044
      priority: 99, // it needs to run after the attributes are interpolated
18045
      link: function(scope, element, attr) {
18046
        var propName = attrName,
18047
            name = attrName;
18048
 
18049
        if (attrName === 'href' &&
18050
            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
18051
          name = 'xlinkHref';
18052
          attr.$attr[name] = 'xlink:href';
18053
          propName = null;
18054
        }
18055
 
18056
        attr.$observe(normalized, function(value) {
18057
          if (!value) {
18058
            if (attrName === 'href') {
18059
              attr.$set(name, null);
18060
            }
18061
            return;
18062
          }
18063
 
18064
          attr.$set(name, value);
18065
 
18066
          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
18067
          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
18068
          // to set the property as well to achieve the desired effect.
18069
          // we use attr[attrName] value since $set can sanitize the url.
18070
          if (msie && propName) element.prop(propName, attr[name]);
18071
        });
18072
      }
18073
    };
18074
  };
18075
});
18076
 
18077
/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
18078
 */
18079
var nullFormCtrl = {
18080
  $addControl: noop,
18081
  $$renameControl: nullFormRenameControl,
18082
  $removeControl: noop,
18083
  $setValidity: noop,
18084
  $setDirty: noop,
18085
  $setPristine: noop,
18086
  $setSubmitted: noop
18087
},
18088
SUBMITTED_CLASS = 'ng-submitted';
18089
 
18090
function nullFormRenameControl(control, name) {
18091
  control.$name = name;
18092
}
18093
 
18094
/**
18095
 * @ngdoc type
18096
 * @name form.FormController
18097
 *
18098
 * @property {boolean} $pristine True if user has not interacted with the form yet.
18099
 * @property {boolean} $dirty True if user has already interacted with the form.
18100
 * @property {boolean} $valid True if all of the containing forms and controls are valid.
18101
 * @property {boolean} $invalid True if at least one containing control or form is invalid.
18102
 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
18103
 *
18104
 * @property {Object} $error Is an object hash, containing references to controls or
18105
 *  forms with failing validators, where:
18106
 *
18107
 *  - keys are validation tokens (error names),
18108
 *  - values are arrays of controls or forms that have a failing validator for given error name.
18109
 *
18110
 *  Built-in validation tokens:
18111
 *
18112
 *  - `email`
18113
 *  - `max`
18114
 *  - `maxlength`
18115
 *  - `min`
18116
 *  - `minlength`
18117
 *  - `number`
18118
 *  - `pattern`
18119
 *  - `required`
18120
 *  - `url`
18121
 *  - `date`
18122
 *  - `datetimelocal`
18123
 *  - `time`
18124
 *  - `week`
18125
 *  - `month`
18126
 *
18127
 * @description
18128
 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
18129
 * such as being valid/invalid or dirty/pristine.
18130
 *
18131
 * Each {@link ng.directive:form form} directive creates an instance
18132
 * of `FormController`.
18133
 *
18134
 */
18135
//asks for $scope to fool the BC controller module
18136
FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
18137
function FormController(element, attrs, $scope, $animate, $interpolate) {
18138
  var form = this,
18139
      controls = [];
18140
 
18141
  var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
18142
 
18143
  // init state
18144
  form.$error = {};
18145
  form.$$success = {};
18146
  form.$pending = undefined;
18147
  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
18148
  form.$dirty = false;
18149
  form.$pristine = true;
18150
  form.$valid = true;
18151
  form.$invalid = false;
18152
  form.$submitted = false;
18153
 
18154
  parentForm.$addControl(form);
18155
 
18156
  /**
18157
   * @ngdoc method
18158
   * @name form.FormController#$rollbackViewValue
18159
   *
18160
   * @description
18161
   * Rollback all form controls pending updates to the `$modelValue`.
18162
   *
18163
   * Updates may be pending by a debounced event or because the input is waiting for a some future
18164
   * event defined in `ng-model-options`. This method is typically needed by the reset button of
18165
   * a form that uses `ng-model-options` to pend updates.
18166
   */
18167
  form.$rollbackViewValue = function() {
18168
    forEach(controls, function(control) {
18169
      control.$rollbackViewValue();
18170
    });
18171
  };
18172
 
18173
  /**
18174
   * @ngdoc method
18175
   * @name form.FormController#$commitViewValue
18176
   *
18177
   * @description
18178
   * Commit all form controls pending updates to the `$modelValue`.
18179
   *
18180
   * Updates may be pending by a debounced event or because the input is waiting for a some future
18181
   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
18182
   * usually handles calling this in response to input events.
18183
   */
18184
  form.$commitViewValue = function() {
18185
    forEach(controls, function(control) {
18186
      control.$commitViewValue();
18187
    });
18188
  };
18189
 
18190
  /**
18191
   * @ngdoc method
18192
   * @name form.FormController#$addControl
18193
   *
18194
   * @description
18195
   * Register a control with the form.
18196
   *
18197
   * Input elements using ngModelController do this automatically when they are linked.
18198
   */
18199
  form.$addControl = function(control) {
18200
    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
18201
    // and not added to the scope.  Now we throw an error.
18202
    assertNotHasOwnProperty(control.$name, 'input');
18203
    controls.push(control);
18204
 
18205
    if (control.$name) {
18206
      form[control.$name] = control;
18207
    }
18208
  };
18209
 
18210
  // Private API: rename a form control
18211
  form.$$renameControl = function(control, newName) {
18212
    var oldName = control.$name;
18213
 
18214
    if (form[oldName] === control) {
18215
      delete form[oldName];
18216
    }
18217
    form[newName] = control;
18218
    control.$name = newName;
18219
  };
18220
 
18221
  /**
18222
   * @ngdoc method
18223
   * @name form.FormController#$removeControl
18224
   *
18225
   * @description
18226
   * Deregister a control from the form.
18227
   *
18228
   * Input elements using ngModelController do this automatically when they are destroyed.
18229
   */
18230
  form.$removeControl = function(control) {
18231
    if (control.$name && form[control.$name] === control) {
18232
      delete form[control.$name];
18233
    }
18234
    forEach(form.$pending, function(value, name) {
18235
      form.$setValidity(name, null, control);
18236
    });
18237
    forEach(form.$error, function(value, name) {
18238
      form.$setValidity(name, null, control);
18239
    });
18240
 
18241
    arrayRemove(controls, control);
18242
  };
18243
 
18244
 
18245
  /**
18246
   * @ngdoc method
18247
   * @name form.FormController#$setValidity
18248
   *
18249
   * @description
18250
   * Sets the validity of a form control.
18251
   *
18252
   * This method will also propagate to parent forms.
18253
   */
18254
  addSetValidityMethod({
18255
    ctrl: this,
18256
    $element: element,
18257
    set: function(object, property, control) {
18258
      var list = object[property];
18259
      if (!list) {
18260
        object[property] = [control];
18261
      } else {
18262
        var index = list.indexOf(control);
18263
        if (index === -1) {
18264
          list.push(control);
18265
        }
18266
      }
18267
    },
18268
    unset: function(object, property, control) {
18269
      var list = object[property];
18270
      if (!list) {
18271
        return;
18272
      }
18273
      arrayRemove(list, control);
18274
      if (list.length === 0) {
18275
        delete object[property];
18276
      }
18277
    },
18278
    parentForm: parentForm,
18279
    $animate: $animate
18280
  });
18281
 
18282
  /**
18283
   * @ngdoc method
18284
   * @name form.FormController#$setDirty
18285
   *
18286
   * @description
18287
   * Sets the form to a dirty state.
18288
   *
18289
   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
18290
   * state (ng-dirty class). This method will also propagate to parent forms.
18291
   */
18292
  form.$setDirty = function() {
18293
    $animate.removeClass(element, PRISTINE_CLASS);
18294
    $animate.addClass(element, DIRTY_CLASS);
18295
    form.$dirty = true;
18296
    form.$pristine = false;
18297
    parentForm.$setDirty();
18298
  };
18299
 
18300
  /**
18301
   * @ngdoc method
18302
   * @name form.FormController#$setPristine
18303
   *
18304
   * @description
18305
   * Sets the form to its pristine state.
18306
   *
18307
   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
18308
   * state (ng-pristine class). This method will also propagate to all the controls contained
18309
   * in this form.
18310
   *
18311
   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
18312
   * saving or resetting it.
18313
   */
18314
  form.$setPristine = function() {
18315
    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
18316
    form.$dirty = false;
18317
    form.$pristine = true;
18318
    form.$submitted = false;
18319
    forEach(controls, function(control) {
18320
      control.$setPristine();
18321
    });
18322
  };
18323
 
18324
  /**
18325
   * @ngdoc method
18326
   * @name form.FormController#$setUntouched
18327
   *
18328
   * @description
18329
   * Sets the form to its untouched state.
18330
   *
18331
   * This method can be called to remove the 'ng-touched' class and set the form controls to their
18332
   * untouched state (ng-untouched class).
18333
   *
18334
   * Setting a form controls back to their untouched state is often useful when setting the form
18335
   * back to its pristine state.
18336
   */
18337
  form.$setUntouched = function() {
18338
    forEach(controls, function(control) {
18339
      control.$setUntouched();
18340
    });
18341
  };
18342
 
18343
  /**
18344
   * @ngdoc method
18345
   * @name form.FormController#$setSubmitted
18346
   *
18347
   * @description
18348
   * Sets the form to its submitted state.
18349
   */
18350
  form.$setSubmitted = function() {
18351
    $animate.addClass(element, SUBMITTED_CLASS);
18352
    form.$submitted = true;
18353
    parentForm.$setSubmitted();
18354
  };
18355
}
18356
 
18357
/**
18358
 * @ngdoc directive
18359
 * @name ngForm
18360
 * @restrict EAC
18361
 *
18362
 * @description
18363
 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
18364
 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
18365
 * sub-group of controls needs to be determined.
18366
 *
18367
 * Note: the purpose of `ngForm` is to group controls,
18368
 * but not to be a replacement for the `<form>` tag with all of its capabilities
18369
 * (e.g. posting to the server, ...).
18370
 *
18371
 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
18372
 *                       related scope, under this name.
18373
 *
18374
 */
18375
 
18376
 /**
18377
 * @ngdoc directive
18378
 * @name form
18379
 * @restrict E
18380
 *
18381
 * @description
18382
 * Directive that instantiates
18383
 * {@link form.FormController FormController}.
18384
 *
18385
 * If the `name` attribute is specified, the form controller is published onto the current scope under
18386
 * this name.
18387
 *
18388
 * # Alias: {@link ng.directive:ngForm `ngForm`}
18389
 *
18390
 * In Angular forms can be nested. This means that the outer form is valid when all of the child
18391
 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
18392
 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
18393
 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
18394
 * using Angular validation directives in forms that are dynamically generated using the
18395
 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
18396
 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
18397
 * `ngForm` directive and nest these in an outer `form` element.
18398
 *
18399
 *
18400
 * # CSS classes
18401
 *  - `ng-valid` is set if the form is valid.
18402
 *  - `ng-invalid` is set if the form is invalid.
18403
 *  - `ng-pristine` is set if the form is pristine.
18404
 *  - `ng-dirty` is set if the form is dirty.
18405
 *  - `ng-submitted` is set if the form was submitted.
18406
 *
18407
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
18408
 *
18409
 *
18410
 * # Submitting a form and preventing the default action
18411
 *
18412
 * Since the role of forms in client-side Angular applications is different than in classical
18413
 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
18414
 * page reload that sends the data to the server. Instead some javascript logic should be triggered
18415
 * to handle the form submission in an application-specific way.
18416
 *
18417
 * For this reason, Angular prevents the default action (form submission to the server) unless the
18418
 * `<form>` element has an `action` attribute specified.
18419
 *
18420
 * You can use one of the following two ways to specify what javascript method should be called when
18421
 * a form is submitted:
18422
 *
18423
 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
18424
 * - {@link ng.directive:ngClick ngClick} directive on the first
18425
  *  button or input field of type submit (input[type=submit])
18426
 *
18427
 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
18428
 * or {@link ng.directive:ngClick ngClick} directives.
18429
 * This is because of the following form submission rules in the HTML specification:
18430
 *
18431
 * - If a form has only one input field then hitting enter in this field triggers form submit
18432
 * (`ngSubmit`)
18433
 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
18434
 * doesn't trigger submit
18435
 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
18436
 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
18437
 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
18438
 *
18439
 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
18440
 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
18441
 * to have access to the updated model.
18442
 *
18443
 * ## Animation Hooks
18444
 *
18445
 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
18446
 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
18447
 * other validations that are performed within the form. Animations in ngForm are similar to how
18448
 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
18449
 * as JS animations.
18450
 *
18451
 * The following example shows a simple way to utilize CSS transitions to style a form element
18452
 * that has been rendered as invalid after it has been validated:
18453
 *
18454
 * <pre>
18455
 * //be sure to include ngAnimate as a module to hook into more
18456
 * //advanced animations
18457
 * .my-form {
18458
 *   transition:0.5s linear all;
18459
 *   background: white;
18460
 * }
18461
 * .my-form.ng-invalid {
18462
 *   background: red;
18463
 *   color:white;
18464
 * }
18465
 * </pre>
18466
 *
18467
 * @example
18468
    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
18469
      <file name="index.html">
18470
       <script>
18471
         angular.module('formExample', [])
18472
           .controller('FormController', ['$scope', function($scope) {
18473
             $scope.userType = 'guest';
18474
           }]);
18475
       </script>
18476
       <style>
18477
        .my-form {
18478
          -webkit-transition:all linear 0.5s;
18479
          transition:all linear 0.5s;
18480
          background: transparent;
18481
        }
18482
        .my-form.ng-invalid {
18483
          background: red;
18484
        }
18485
       </style>
18486
       <form name="myForm" ng-controller="FormController" class="my-form">
18487
         userType: <input name="input" ng-model="userType" required>
18488
         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
18489
         <tt>userType = {{userType}}</tt><br>
18490
         <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
18491
         <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
18492
         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
18493
         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
18494
        </form>
18495
      </file>
18496
      <file name="protractor.js" type="protractor">
18497
        it('should initialize to model', function() {
18498
          var userType = element(by.binding('userType'));
18499
          var valid = element(by.binding('myForm.input.$valid'));
18500
 
18501
          expect(userType.getText()).toContain('guest');
18502
          expect(valid.getText()).toContain('true');
18503
        });
18504
 
18505
        it('should be invalid if empty', function() {
18506
          var userType = element(by.binding('userType'));
18507
          var valid = element(by.binding('myForm.input.$valid'));
18508
          var userInput = element(by.model('userType'));
18509
 
18510
          userInput.clear();
18511
          userInput.sendKeys('');
18512
 
18513
          expect(userType.getText()).toEqual('userType =');
18514
          expect(valid.getText()).toContain('false');
18515
        });
18516
      </file>
18517
    </example>
18518
 *
18519
 * @param {string=} name Name of the form. If specified, the form controller will be published into
18520
 *                       related scope, under this name.
18521
 */
18522
var formDirectiveFactory = function(isNgForm) {
18523
  return ['$timeout', function($timeout) {
18524
    var formDirective = {
18525
      name: 'form',
18526
      restrict: isNgForm ? 'EAC' : 'E',
18527
      controller: FormController,
18528
      compile: function ngFormCompile(formElement) {
18529
        // Setup initial state of the control
18530
        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
18531
 
18532
        return {
18533
          pre: function ngFormPreLink(scope, formElement, attr, controller) {
18534
            // if `action` attr is not present on the form, prevent the default action (submission)
18535
            if (!('action' in attr)) {
18536
              // we can't use jq events because if a form is destroyed during submission the default
18537
              // action is not prevented. see #1238
18538
              //
18539
              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
18540
              // page reload if the form was destroyed by submission of the form via a click handler
18541
              // on a button in the form. Looks like an IE9 specific bug.
18542
              var handleFormSubmission = function(event) {
18543
                scope.$apply(function() {
18544
                  controller.$commitViewValue();
18545
                  controller.$setSubmitted();
18546
                });
18547
 
18548
                event.preventDefault();
18549
              };
18550
 
18551
              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18552
 
18553
              // unregister the preventDefault listener so that we don't not leak memory but in a
18554
              // way that will achieve the prevention of the default action.
18555
              formElement.on('$destroy', function() {
18556
                $timeout(function() {
18557
                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18558
                }, 0, false);
18559
              });
18560
            }
18561
 
18562
            var parentFormCtrl = controller.$$parentForm,
18563
                alias = controller.$name;
18564
 
18565
            if (alias) {
18566
              setter(scope, alias, controller, alias);
18567
              attr.$observe(attr.name ? 'name' : 'ngForm', function(newValue) {
18568
                if (alias === newValue) return;
18569
                setter(scope, alias, undefined, alias);
18570
                alias = newValue;
18571
                setter(scope, alias, controller, alias);
18572
                parentFormCtrl.$$renameControl(controller, alias);
18573
              });
18574
            }
18575
            formElement.on('$destroy', function() {
18576
              parentFormCtrl.$removeControl(controller);
18577
              if (alias) {
18578
                setter(scope, alias, undefined, alias);
18579
              }
18580
              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
18581
            });
18582
          }
18583
        };
18584
      }
18585
    };
18586
 
18587
    return formDirective;
18588
  }];
18589
};
18590
 
18591
var formDirective = formDirectiveFactory();
18592
var ngFormDirective = formDirectiveFactory(true);
18593
 
18594
/* global VALID_CLASS: true,
18595
  INVALID_CLASS: true,
18596
  PRISTINE_CLASS: true,
18597
  DIRTY_CLASS: true,
18598
  UNTOUCHED_CLASS: true,
18599
  TOUCHED_CLASS: true,
18600
*/
18601
 
18602
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
18603
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)/;
18604
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
18605
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;
18606
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
18607
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
18608
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18609
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
18610
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
18611
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18612
var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
18613
 
18614
var $ngModelMinErr = new minErr('ngModel');
18615
 
18616
var inputType = {
18617
 
18618
  /**
18619
   * @ngdoc input
18620
   * @name input[text]
18621
   *
18622
   * @description
18623
   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
18624
   *
18625
   *
18626
   * @param {string} ngModel Assignable angular expression to data-bind to.
18627
   * @param {string=} name Property name of the form under which the control is published.
18628
   * @param {string=} required Adds `required` validation error key if the value is not entered.
18629
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18630
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18631
   *    `required` when you want to data-bind to the `required` attribute.
18632
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18633
   *    minlength.
18634
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18635
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18636
   *    any length.
18637
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18638
   *    that contains the regular expression body that will be converted to a regular expression
18639
   *    as in the ngPattern directive.
18640
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18641
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
18642
   *    If the expression evaluates to a RegExp object then this is used directly.
18643
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18644
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18645
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
18646
   *    interaction with the input element.
18647
   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
18648
   *    This parameter is ignored for input[type=password] controls, which will never trim the
18649
   *    input.
18650
   *
18651
   * @example
18652
      <example name="text-input-directive" module="textInputExample">
18653
        <file name="index.html">
18654
         <script>
18655
           angular.module('textInputExample', [])
18656
             .controller('ExampleController', ['$scope', function($scope) {
18657
               $scope.text = 'guest';
18658
               $scope.word = /^\s*\w*\s*$/;
18659
             }]);
18660
         </script>
18661
         <form name="myForm" ng-controller="ExampleController">
18662
           Single word: <input type="text" name="input" ng-model="text"
18663
                               ng-pattern="word" required ng-trim="false">
18664
           <span class="error" ng-show="myForm.input.$error.required">
18665
             Required!</span>
18666
           <span class="error" ng-show="myForm.input.$error.pattern">
18667
             Single word only!</span>
18668
 
18669
           <tt>text = {{text}}</tt><br/>
18670
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18671
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18672
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18673
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18674
          </form>
18675
        </file>
18676
        <file name="protractor.js" type="protractor">
18677
          var text = element(by.binding('text'));
18678
          var valid = element(by.binding('myForm.input.$valid'));
18679
          var input = element(by.model('text'));
18680
 
18681
          it('should initialize to model', function() {
18682
            expect(text.getText()).toContain('guest');
18683
            expect(valid.getText()).toContain('true');
18684
          });
18685
 
18686
          it('should be invalid if empty', function() {
18687
            input.clear();
18688
            input.sendKeys('');
18689
 
18690
            expect(text.getText()).toEqual('text =');
18691
            expect(valid.getText()).toContain('false');
18692
          });
18693
 
18694
          it('should be invalid if multi word', function() {
18695
            input.clear();
18696
            input.sendKeys('hello world');
18697
 
18698
            expect(valid.getText()).toContain('false');
18699
          });
18700
        </file>
18701
      </example>
18702
   */
18703
  'text': textInputType,
18704
 
18705
    /**
18706
     * @ngdoc input
18707
     * @name input[date]
18708
     *
18709
     * @description
18710
     * Input with date validation and transformation. In browsers that do not yet support
18711
     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
18712
     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
18713
     * modern browsers do not yet support this input type, it is important to provide cues to users on the
18714
     * expected input format via a placeholder or label.
18715
     *
18716
     * The model must always be a Date object, otherwise Angular will throw an error.
18717
     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18718
     *
18719
     * The timezone to be used to read/write the `Date` instance in the model can be defined using
18720
     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18721
     *
18722
     * @param {string} ngModel Assignable angular expression to data-bind to.
18723
     * @param {string=} name Property name of the form under which the control is published.
18724
     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18725
     * valid ISO date string (yyyy-MM-dd).
18726
     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18727
     * a valid ISO date string (yyyy-MM-dd).
18728
     * @param {string=} required Sets `required` validation error key if the value is not entered.
18729
     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18730
     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18731
     *    `required` when you want to data-bind to the `required` attribute.
18732
     * @param {string=} ngChange Angular expression to be executed when input changes due to user
18733
     *    interaction with the input element.
18734
     *
18735
     * @example
18736
     <example name="date-input-directive" module="dateInputExample">
18737
     <file name="index.html">
18738
       <script>
18739
          angular.module('dateInputExample', [])
18740
            .controller('DateController', ['$scope', function($scope) {
18741
              $scope.value = new Date(2013, 9, 22);
18742
            }]);
18743
       </script>
18744
       <form name="myForm" ng-controller="DateController as dateCtrl">
18745
          Pick a date in 2013:
18746
          <input type="date" id="exampleInput" name="input" ng-model="value"
18747
              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
18748
          <span class="error" ng-show="myForm.input.$error.required">
18749
              Required!</span>
18750
          <span class="error" ng-show="myForm.input.$error.date">
18751
              Not a valid date!</span>
18752
           <tt>value = {{value | date: "yyyy-MM-dd"}}</tt><br/>
18753
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18754
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18755
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18756
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18757
       </form>
18758
     </file>
18759
     <file name="protractor.js" type="protractor">
18760
        var value = element(by.binding('value | date: "yyyy-MM-dd"'));
18761
        var valid = element(by.binding('myForm.input.$valid'));
18762
        var input = element(by.model('value'));
18763
 
18764
        // currently protractor/webdriver does not support
18765
        // sending keys to all known HTML5 input controls
18766
        // for various browsers (see https://github.com/angular/protractor/issues/562).
18767
        function setInput(val) {
18768
          // set the value of the element and force validation.
18769
          var scr = "var ipt = document.getElementById('exampleInput'); " +
18770
          "ipt.value = '" + val + "';" +
18771
          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18772
          browser.executeScript(scr);
18773
        }
18774
 
18775
        it('should initialize to model', function() {
18776
          expect(value.getText()).toContain('2013-10-22');
18777
          expect(valid.getText()).toContain('myForm.input.$valid = true');
18778
        });
18779
 
18780
        it('should be invalid if empty', function() {
18781
          setInput('');
18782
          expect(value.getText()).toEqual('value =');
18783
          expect(valid.getText()).toContain('myForm.input.$valid = false');
18784
        });
18785
 
18786
        it('should be invalid if over max', function() {
18787
          setInput('2015-01-01');
18788
          expect(value.getText()).toContain('');
18789
          expect(valid.getText()).toContain('myForm.input.$valid = false');
18790
        });
18791
     </file>
18792
     </example>
18793
     */
18794
  'date': createDateInputType('date', DATE_REGEXP,
18795
         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
18796
         'yyyy-MM-dd'),
18797
 
18798
   /**
18799
    * @ngdoc input
18800
    * @name input[datetime-local]
18801
    *
18802
    * @description
18803
    * Input with datetime validation and transformation. In browsers that do not yet support
18804
    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18805
    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
18806
    *
18807
    * The model must always be a Date object, otherwise Angular will throw an error.
18808
    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18809
    *
18810
    * The timezone to be used to read/write the `Date` instance in the model can be defined using
18811
    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18812
    *
18813
    * @param {string} ngModel Assignable angular expression to data-bind to.
18814
    * @param {string=} name Property name of the form under which the control is published.
18815
    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18816
    * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18817
    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18818
    * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18819
    * @param {string=} required Sets `required` validation error key if the value is not entered.
18820
    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18821
    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18822
    *    `required` when you want to data-bind to the `required` attribute.
18823
    * @param {string=} ngChange Angular expression to be executed when input changes due to user
18824
    *    interaction with the input element.
18825
    *
18826
    * @example
18827
    <example name="datetimelocal-input-directive" module="dateExample">
18828
    <file name="index.html">
18829
      <script>
18830
        angular.module('dateExample', [])
18831
          .controller('DateController', ['$scope', function($scope) {
18832
            $scope.value = new Date(2010, 11, 28, 14, 57);
18833
          }]);
18834
      </script>
18835
      <form name="myForm" ng-controller="DateController as dateCtrl">
18836
        Pick a date between in 2013:
18837
        <input type="datetime-local" id="exampleInput" name="input" ng-model="value"
18838
            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
18839
        <span class="error" ng-show="myForm.input.$error.required">
18840
            Required!</span>
18841
        <span class="error" ng-show="myForm.input.$error.datetimelocal">
18842
            Not a valid date!</span>
18843
        <tt>value = {{value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
18844
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18845
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18846
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18847
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18848
      </form>
18849
    </file>
18850
    <file name="protractor.js" type="protractor">
18851
      var value = element(by.binding('value | date: "yyyy-MM-ddTHH:mm:ss"'));
18852
      var valid = element(by.binding('myForm.input.$valid'));
18853
      var input = element(by.model('value'));
18854
 
18855
      // currently protractor/webdriver does not support
18856
      // sending keys to all known HTML5 input controls
18857
      // for various browsers (https://github.com/angular/protractor/issues/562).
18858
      function setInput(val) {
18859
        // set the value of the element and force validation.
18860
        var scr = "var ipt = document.getElementById('exampleInput'); " +
18861
        "ipt.value = '" + val + "';" +
18862
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18863
        browser.executeScript(scr);
18864
      }
18865
 
18866
      it('should initialize to model', function() {
18867
        expect(value.getText()).toContain('2010-12-28T14:57:00');
18868
        expect(valid.getText()).toContain('myForm.input.$valid = true');
18869
      });
18870
 
18871
      it('should be invalid if empty', function() {
18872
        setInput('');
18873
        expect(value.getText()).toEqual('value =');
18874
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18875
      });
18876
 
18877
      it('should be invalid if over max', function() {
18878
        setInput('2015-01-01T23:59:00');
18879
        expect(value.getText()).toContain('');
18880
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18881
      });
18882
    </file>
18883
    </example>
18884
    */
18885
  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
18886
      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
18887
      'yyyy-MM-ddTHH:mm:ss.sss'),
18888
 
18889
  /**
18890
   * @ngdoc input
18891
   * @name input[time]
18892
   *
18893
   * @description
18894
   * Input with time validation and transformation. In browsers that do not yet support
18895
   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18896
   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
18897
   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
18898
   *
18899
   * The model must always be a Date object, otherwise Angular will throw an error.
18900
   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18901
   *
18902
   * The timezone to be used to read/write the `Date` instance in the model can be defined using
18903
   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18904
   *
18905
   * @param {string} ngModel Assignable angular expression to data-bind to.
18906
   * @param {string=} name Property name of the form under which the control is published.
18907
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18908
   * valid ISO time format (HH:mm:ss).
18909
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
18910
   * valid ISO time format (HH:mm:ss).
18911
   * @param {string=} required Sets `required` validation error key if the value is not entered.
18912
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18913
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18914
   *    `required` when you want to data-bind to the `required` attribute.
18915
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
18916
   *    interaction with the input element.
18917
   *
18918
   * @example
18919
   <example name="time-input-directive" module="timeExample">
18920
   <file name="index.html">
18921
     <script>
18922
      angular.module('timeExample', [])
18923
        .controller('DateController', ['$scope', function($scope) {
18924
          $scope.value = new Date(1970, 0, 1, 14, 57, 0);
18925
        }]);
18926
     </script>
18927
     <form name="myForm" ng-controller="DateController as dateCtrl">
18928
        Pick a between 8am and 5pm:
18929
        <input type="time" id="exampleInput" name="input" ng-model="value"
18930
            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
18931
        <span class="error" ng-show="myForm.input.$error.required">
18932
            Required!</span>
18933
        <span class="error" ng-show="myForm.input.$error.time">
18934
            Not a valid date!</span>
18935
        <tt>value = {{value | date: "HH:mm:ss"}}</tt><br/>
18936
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18937
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18938
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18939
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18940
     </form>
18941
   </file>
18942
   <file name="protractor.js" type="protractor">
18943
      var value = element(by.binding('value | date: "HH:mm:ss"'));
18944
      var valid = element(by.binding('myForm.input.$valid'));
18945
      var input = element(by.model('value'));
18946
 
18947
      // currently protractor/webdriver does not support
18948
      // sending keys to all known HTML5 input controls
18949
      // for various browsers (https://github.com/angular/protractor/issues/562).
18950
      function setInput(val) {
18951
        // set the value of the element and force validation.
18952
        var scr = "var ipt = document.getElementById('exampleInput'); " +
18953
        "ipt.value = '" + val + "';" +
18954
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18955
        browser.executeScript(scr);
18956
      }
18957
 
18958
      it('should initialize to model', function() {
18959
        expect(value.getText()).toContain('14:57:00');
18960
        expect(valid.getText()).toContain('myForm.input.$valid = true');
18961
      });
18962
 
18963
      it('should be invalid if empty', function() {
18964
        setInput('');
18965
        expect(value.getText()).toEqual('value =');
18966
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18967
      });
18968
 
18969
      it('should be invalid if over max', function() {
18970
        setInput('23:59:00');
18971
        expect(value.getText()).toContain('');
18972
        expect(valid.getText()).toContain('myForm.input.$valid = false');
18973
      });
18974
   </file>
18975
   </example>
18976
   */
18977
  'time': createDateInputType('time', TIME_REGEXP,
18978
      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
18979
     'HH:mm:ss.sss'),
18980
 
18981
   /**
18982
    * @ngdoc input
18983
    * @name input[week]
18984
    *
18985
    * @description
18986
    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
18987
    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18988
    * week format (yyyy-W##), for example: `2013-W02`.
18989
    *
18990
    * The model must always be a Date object, otherwise Angular will throw an error.
18991
    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18992
    *
18993
    * The timezone to be used to read/write the `Date` instance in the model can be defined using
18994
    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18995
    *
18996
    * @param {string} ngModel Assignable angular expression to data-bind to.
18997
    * @param {string=} name Property name of the form under which the control is published.
18998
    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18999
    * valid ISO week format (yyyy-W##).
19000
    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19001
    * a valid ISO week format (yyyy-W##).
19002
    * @param {string=} required Sets `required` validation error key if the value is not entered.
19003
    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19004
    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19005
    *    `required` when you want to data-bind to the `required` attribute.
19006
    * @param {string=} ngChange Angular expression to be executed when input changes due to user
19007
    *    interaction with the input element.
19008
    *
19009
    * @example
19010
    <example name="week-input-directive" module="weekExample">
19011
    <file name="index.html">
19012
      <script>
19013
      angular.module('weekExample', [])
19014
        .controller('DateController', ['$scope', function($scope) {
19015
          $scope.value = new Date(2013, 0, 3);
19016
        }]);
19017
      </script>
19018
      <form name="myForm" ng-controller="DateController as dateCtrl">
19019
        Pick a date between in 2013:
19020
        <input id="exampleInput" type="week" name="input" ng-model="value"
19021
            placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
19022
        <span class="error" ng-show="myForm.input.$error.required">
19023
            Required!</span>
19024
        <span class="error" ng-show="myForm.input.$error.week">
19025
            Not a valid date!</span>
19026
        <tt>value = {{value | date: "yyyy-Www"}}</tt><br/>
19027
        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19028
        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19029
        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19030
        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19031
      </form>
19032
    </file>
19033
    <file name="protractor.js" type="protractor">
19034
      var value = element(by.binding('value | date: "yyyy-Www"'));
19035
      var valid = element(by.binding('myForm.input.$valid'));
19036
      var input = element(by.model('value'));
19037
 
19038
      // currently protractor/webdriver does not support
19039
      // sending keys to all known HTML5 input controls
19040
      // for various browsers (https://github.com/angular/protractor/issues/562).
19041
      function setInput(val) {
19042
        // set the value of the element and force validation.
19043
        var scr = "var ipt = document.getElementById('exampleInput'); " +
19044
        "ipt.value = '" + val + "';" +
19045
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19046
        browser.executeScript(scr);
19047
      }
19048
 
19049
      it('should initialize to model', function() {
19050
        expect(value.getText()).toContain('2013-W01');
19051
        expect(valid.getText()).toContain('myForm.input.$valid = true');
19052
      });
19053
 
19054
      it('should be invalid if empty', function() {
19055
        setInput('');
19056
        expect(value.getText()).toEqual('value =');
19057
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19058
      });
19059
 
19060
      it('should be invalid if over max', function() {
19061
        setInput('2015-W01');
19062
        expect(value.getText()).toContain('');
19063
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19064
      });
19065
    </file>
19066
    </example>
19067
    */
19068
  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
19069
 
19070
  /**
19071
   * @ngdoc input
19072
   * @name input[month]
19073
   *
19074
   * @description
19075
   * Input with month validation and transformation. In browsers that do not yet support
19076
   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19077
   * month format (yyyy-MM), for example: `2009-01`.
19078
   *
19079
   * The model must always be a Date object, otherwise Angular will throw an error.
19080
   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19081
   * If the model is not set to the first of the month, the next view to model update will set it
19082
   * to the first of the month.
19083
   *
19084
   * The timezone to be used to read/write the `Date` instance in the model can be defined using
19085
   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19086
   *
19087
   * @param {string} ngModel Assignable angular expression to data-bind to.
19088
   * @param {string=} name Property name of the form under which the control is published.
19089
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
19090
   * a valid ISO month format (yyyy-MM).
19091
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
19092
   * be a valid ISO month format (yyyy-MM).
19093
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19094
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19095
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19096
   *    `required` when you want to data-bind to the `required` attribute.
19097
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19098
   *    interaction with the input element.
19099
   *
19100
   * @example
19101
   <example name="month-input-directive" module="monthExample">
19102
   <file name="index.html">
19103
     <script>
19104
      angular.module('monthExample', [])
19105
        .controller('DateController', ['$scope', function($scope) {
19106
          $scope.value = new Date(2013, 9, 1);
19107
        }]);
19108
     </script>
19109
     <form name="myForm" ng-controller="DateController as dateCtrl">
19110
       Pick a month int 2013:
19111
       <input id="exampleInput" type="month" name="input" ng-model="value"
19112
          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
19113
       <span class="error" ng-show="myForm.input.$error.required">
19114
          Required!</span>
19115
       <span class="error" ng-show="myForm.input.$error.month">
19116
          Not a valid month!</span>
19117
       <tt>value = {{value | date: "yyyy-MM"}}</tt><br/>
19118
       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19119
       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19120
       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19121
       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19122
     </form>
19123
   </file>
19124
   <file name="protractor.js" type="protractor">
19125
      var value = element(by.binding('value | date: "yyyy-MM"'));
19126
      var valid = element(by.binding('myForm.input.$valid'));
19127
      var input = element(by.model('value'));
19128
 
19129
      // currently protractor/webdriver does not support
19130
      // sending keys to all known HTML5 input controls
19131
      // for various browsers (https://github.com/angular/protractor/issues/562).
19132
      function setInput(val) {
19133
        // set the value of the element and force validation.
19134
        var scr = "var ipt = document.getElementById('exampleInput'); " +
19135
        "ipt.value = '" + val + "';" +
19136
        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19137
        browser.executeScript(scr);
19138
      }
19139
 
19140
      it('should initialize to model', function() {
19141
        expect(value.getText()).toContain('2013-10');
19142
        expect(valid.getText()).toContain('myForm.input.$valid = true');
19143
      });
19144
 
19145
      it('should be invalid if empty', function() {
19146
        setInput('');
19147
        expect(value.getText()).toEqual('value =');
19148
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19149
      });
19150
 
19151
      it('should be invalid if over max', function() {
19152
        setInput('2015-01');
19153
        expect(value.getText()).toContain('');
19154
        expect(valid.getText()).toContain('myForm.input.$valid = false');
19155
      });
19156
   </file>
19157
   </example>
19158
   */
19159
  'month': createDateInputType('month', MONTH_REGEXP,
19160
     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
19161
     'yyyy-MM'),
19162
 
19163
  /**
19164
   * @ngdoc input
19165
   * @name input[number]
19166
   *
19167
   * @description
19168
   * Text input with number validation and transformation. Sets the `number` validation
19169
   * error if not a valid number.
19170
   *
19171
   * The model must always be a number, otherwise Angular will throw an error.
19172
   *
19173
   * @param {string} ngModel Assignable angular expression to data-bind to.
19174
   * @param {string=} name Property name of the form under which the control is published.
19175
   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
19176
   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
19177
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19178
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19179
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19180
   *    `required` when you want to data-bind to the `required` attribute.
19181
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19182
   *    minlength.
19183
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19184
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19185
   *    any length.
19186
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19187
   *    that contains the regular expression body that will be converted to a regular expression
19188
   *    as in the ngPattern directive.
19189
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19190
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19191
   *    If the expression evaluates to a RegExp object then this is used directly.
19192
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19193
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19194
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19195
   *    interaction with the input element.
19196
   *
19197
   * @example
19198
      <example name="number-input-directive" module="numberExample">
19199
        <file name="index.html">
19200
         <script>
19201
           angular.module('numberExample', [])
19202
             .controller('ExampleController', ['$scope', function($scope) {
19203
               $scope.value = 12;
19204
             }]);
19205
         </script>
19206
         <form name="myForm" ng-controller="ExampleController">
19207
           Number: <input type="number" name="input" ng-model="value"
19208
                          min="0" max="99" required>
19209
           <span class="error" ng-show="myForm.input.$error.required">
19210
             Required!</span>
19211
           <span class="error" ng-show="myForm.input.$error.number">
19212
             Not valid number!</span>
19213
           <tt>value = {{value}}</tt><br/>
19214
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19215
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19216
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19217
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19218
          </form>
19219
        </file>
19220
        <file name="protractor.js" type="protractor">
19221
          var value = element(by.binding('value'));
19222
          var valid = element(by.binding('myForm.input.$valid'));
19223
          var input = element(by.model('value'));
19224
 
19225
          it('should initialize to model', function() {
19226
            expect(value.getText()).toContain('12');
19227
            expect(valid.getText()).toContain('true');
19228
          });
19229
 
19230
          it('should be invalid if empty', function() {
19231
            input.clear();
19232
            input.sendKeys('');
19233
            expect(value.getText()).toEqual('value =');
19234
            expect(valid.getText()).toContain('false');
19235
          });
19236
 
19237
          it('should be invalid if over max', function() {
19238
            input.clear();
19239
            input.sendKeys('123');
19240
            expect(value.getText()).toEqual('value =');
19241
            expect(valid.getText()).toContain('false');
19242
          });
19243
        </file>
19244
      </example>
19245
   */
19246
  'number': numberInputType,
19247
 
19248
 
19249
  /**
19250
   * @ngdoc input
19251
   * @name input[url]
19252
   *
19253
   * @description
19254
   * Text input with URL validation. Sets the `url` validation error key if the content is not a
19255
   * valid URL.
19256
   *
19257
   * <div class="alert alert-warning">
19258
   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
19259
   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
19260
   * the built-in validators (see the {@link guide/forms Forms guide})
19261
   * </div>
19262
   *
19263
   * @param {string} ngModel Assignable angular expression to data-bind to.
19264
   * @param {string=} name Property name of the form under which the control is published.
19265
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19266
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19267
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19268
   *    `required` when you want to data-bind to the `required` attribute.
19269
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19270
   *    minlength.
19271
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19272
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19273
   *    any length.
19274
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19275
   *    that contains the regular expression body that will be converted to a regular expression
19276
   *    as in the ngPattern directive.
19277
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19278
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19279
   *    If the expression evaluates to a RegExp object then this is used directly.
19280
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19281
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19282
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19283
   *    interaction with the input element.
19284
   *
19285
   * @example
19286
      <example name="url-input-directive" module="urlExample">
19287
        <file name="index.html">
19288
         <script>
19289
           angular.module('urlExample', [])
19290
             .controller('ExampleController', ['$scope', function($scope) {
19291
               $scope.text = 'http://google.com';
19292
             }]);
19293
         </script>
19294
         <form name="myForm" ng-controller="ExampleController">
19295
           URL: <input type="url" name="input" ng-model="text" required>
19296
           <span class="error" ng-show="myForm.input.$error.required">
19297
             Required!</span>
19298
           <span class="error" ng-show="myForm.input.$error.url">
19299
             Not valid url!</span>
19300
           <tt>text = {{text}}</tt><br/>
19301
           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19302
           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19303
           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19304
           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19305
           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
19306
          </form>
19307
        </file>
19308
        <file name="protractor.js" type="protractor">
19309
          var text = element(by.binding('text'));
19310
          var valid = element(by.binding('myForm.input.$valid'));
19311
          var input = element(by.model('text'));
19312
 
19313
          it('should initialize to model', function() {
19314
            expect(text.getText()).toContain('http://google.com');
19315
            expect(valid.getText()).toContain('true');
19316
          });
19317
 
19318
          it('should be invalid if empty', function() {
19319
            input.clear();
19320
            input.sendKeys('');
19321
 
19322
            expect(text.getText()).toEqual('text =');
19323
            expect(valid.getText()).toContain('false');
19324
          });
19325
 
19326
          it('should be invalid if not url', function() {
19327
            input.clear();
19328
            input.sendKeys('box');
19329
 
19330
            expect(valid.getText()).toContain('false');
19331
          });
19332
        </file>
19333
      </example>
19334
   */
19335
  'url': urlInputType,
19336
 
19337
 
19338
  /**
19339
   * @ngdoc input
19340
   * @name input[email]
19341
   *
19342
   * @description
19343
   * Text input with email validation. Sets the `email` validation error key if not a valid email
19344
   * address.
19345
   *
19346
   * <div class="alert alert-warning">
19347
   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
19348
   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
19349
   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
19350
   * </div>
19351
   *
19352
   * @param {string} ngModel Assignable angular expression to data-bind to.
19353
   * @param {string=} name Property name of the form under which the control is published.
19354
   * @param {string=} required Sets `required` validation error key if the value is not entered.
19355
   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19356
   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19357
   *    `required` when you want to data-bind to the `required` attribute.
19358
   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19359
   *    minlength.
19360
   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19361
   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19362
   *    any length.
19363
   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19364
   *    that contains the regular expression body that will be converted to a regular expression
19365
   *    as in the ngPattern directive.
19366
   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19367
   *    a RegExp found by evaluating the Angular expression given in the attribute value.
19368
   *    If the expression evaluates to a RegExp object then this is used directly.
19369
   *    If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19370
   *    characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19371
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19372
   *    interaction with the input element.
19373
   *
19374
   * @example
19375
      <example name="email-input-directive" module="emailExample">
19376
        <file name="index.html">
19377
         <script>
19378
           angular.module('emailExample', [])
19379
             .controller('ExampleController', ['$scope', function($scope) {
19380
               $scope.text = 'me@example.com';
19381
             }]);
19382
         </script>
19383
           <form name="myForm" ng-controller="ExampleController">
19384
             Email: <input type="email" name="input" ng-model="text" required>
19385
             <span class="error" ng-show="myForm.input.$error.required">
19386
               Required!</span>
19387
             <span class="error" ng-show="myForm.input.$error.email">
19388
               Not valid email!</span>
19389
             <tt>text = {{text}}</tt><br/>
19390
             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19391
             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19392
             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19393
             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19394
             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
19395
           </form>
19396
         </file>
19397
        <file name="protractor.js" type="protractor">
19398
          var text = element(by.binding('text'));
19399
          var valid = element(by.binding('myForm.input.$valid'));
19400
          var input = element(by.model('text'));
19401
 
19402
          it('should initialize to model', function() {
19403
            expect(text.getText()).toContain('me@example.com');
19404
            expect(valid.getText()).toContain('true');
19405
          });
19406
 
19407
          it('should be invalid if empty', function() {
19408
            input.clear();
19409
            input.sendKeys('');
19410
            expect(text.getText()).toEqual('text =');
19411
            expect(valid.getText()).toContain('false');
19412
          });
19413
 
19414
          it('should be invalid if not email', function() {
19415
            input.clear();
19416
            input.sendKeys('xxx');
19417
 
19418
            expect(valid.getText()).toContain('false');
19419
          });
19420
        </file>
19421
      </example>
19422
   */
19423
  'email': emailInputType,
19424
 
19425
 
19426
  /**
19427
   * @ngdoc input
19428
   * @name input[radio]
19429
   *
19430
   * @description
19431
   * HTML radio button.
19432
   *
19433
   * @param {string} ngModel Assignable angular expression to data-bind to.
19434
   * @param {string} value The value to which the expression should be set when selected.
19435
   * @param {string=} name Property name of the form under which the control is published.
19436
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19437
   *    interaction with the input element.
19438
   * @param {string} ngValue Angular expression which sets the value to which the expression should
19439
   *    be set when selected.
19440
   *
19441
   * @example
19442
      <example name="radio-input-directive" module="radioExample">
19443
        <file name="index.html">
19444
         <script>
19445
           angular.module('radioExample', [])
19446
             .controller('ExampleController', ['$scope', function($scope) {
19447
               $scope.color = 'blue';
19448
               $scope.specialValue = {
19449
                 "id": "12345",
19450
                 "value": "green"
19451
               };
19452
             }]);
19453
         </script>
19454
         <form name="myForm" ng-controller="ExampleController">
19455
           <input type="radio" ng-model="color" value="red">  Red <br/>
19456
           <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
19457
           <input type="radio" ng-model="color" value="blue"> Blue <br/>
19458
           <tt>color = {{color | json}}</tt><br/>
19459
          </form>
19460
          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
19461
        </file>
19462
        <file name="protractor.js" type="protractor">
19463
          it('should change state', function() {
19464
            var color = element(by.binding('color'));
19465
 
19466
            expect(color.getText()).toContain('blue');
19467
 
19468
            element.all(by.model('color')).get(0).click();
19469
 
19470
            expect(color.getText()).toContain('red');
19471
          });
19472
        </file>
19473
      </example>
19474
   */
19475
  'radio': radioInputType,
19476
 
19477
 
19478
  /**
19479
   * @ngdoc input
19480
   * @name input[checkbox]
19481
   *
19482
   * @description
19483
   * HTML checkbox.
19484
   *
19485
   * @param {string} ngModel Assignable angular expression to data-bind to.
19486
   * @param {string=} name Property name of the form under which the control is published.
19487
   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
19488
   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
19489
   * @param {string=} ngChange Angular expression to be executed when input changes due to user
19490
   *    interaction with the input element.
19491
   *
19492
   * @example
19493
      <example name="checkbox-input-directive" module="checkboxExample">
19494
        <file name="index.html">
19495
         <script>
19496
           angular.module('checkboxExample', [])
19497
             .controller('ExampleController', ['$scope', function($scope) {
19498
               $scope.value1 = true;
19499
               $scope.value2 = 'YES'
19500
             }]);
19501
         </script>
19502
         <form name="myForm" ng-controller="ExampleController">
19503
           Value1: <input type="checkbox" ng-model="value1"> <br/>
19504
           Value2: <input type="checkbox" ng-model="value2"
19505
                          ng-true-value="'YES'" ng-false-value="'NO'"> <br/>
19506
           <tt>value1 = {{value1}}</tt><br/>
19507
           <tt>value2 = {{value2}}</tt><br/>
19508
          </form>
19509
        </file>
19510
        <file name="protractor.js" type="protractor">
19511
          it('should change state', function() {
19512
            var value1 = element(by.binding('value1'));
19513
            var value2 = element(by.binding('value2'));
19514
 
19515
            expect(value1.getText()).toContain('true');
19516
            expect(value2.getText()).toContain('YES');
19517
 
19518
            element(by.model('value1')).click();
19519
            element(by.model('value2')).click();
19520
 
19521
            expect(value1.getText()).toContain('false');
19522
            expect(value2.getText()).toContain('NO');
19523
          });
19524
        </file>
19525
      </example>
19526
   */
19527
  'checkbox': checkboxInputType,
19528
 
19529
  'hidden': noop,
19530
  'button': noop,
19531
  'submit': noop,
19532
  'reset': noop,
19533
  'file': noop
19534
};
19535
 
19536
function stringBasedInputType(ctrl) {
19537
  ctrl.$formatters.push(function(value) {
19538
    return ctrl.$isEmpty(value) ? value : value.toString();
19539
  });
19540
}
19541
 
19542
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19543
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19544
  stringBasedInputType(ctrl);
19545
}
19546
 
19547
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19548
  var type = lowercase(element[0].type);
19549
 
19550
  // In composition mode, users are still inputing intermediate text buffer,
19551
  // hold the listener until composition is done.
19552
  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
19553
  if (!$sniffer.android) {
19554
    var composing = false;
19555
 
19556
    element.on('compositionstart', function(data) {
19557
      composing = true;
19558
    });
19559
 
19560
    element.on('compositionend', function() {
19561
      composing = false;
19562
      listener();
19563
    });
19564
  }
19565
 
19566
  var listener = function(ev) {
19567
    if (timeout) {
19568
      $browser.defer.cancel(timeout);
19569
      timeout = null;
19570
    }
19571
    if (composing) return;
19572
    var value = element.val(),
19573
        event = ev && ev.type;
19574
 
19575
    // By default we will trim the value
19576
    // If the attribute ng-trim exists we will avoid trimming
19577
    // If input type is 'password', the value is never trimmed
19578
    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
19579
      value = trim(value);
19580
    }
19581
 
19582
    // If a control is suffering from bad input (due to native validators), browsers discard its
19583
    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
19584
    // control's value is the same empty value twice in a row.
19585
    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
19586
      ctrl.$setViewValue(value, event);
19587
    }
19588
  };
19589
 
19590
  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
19591
  // input event on backspace, delete or cut
19592
  if ($sniffer.hasEvent('input')) {
19593
    element.on('input', listener);
19594
  } else {
19595
    var timeout;
19596
 
19597
    var deferListener = function(ev, input, origValue) {
19598
      if (!timeout) {
19599
        timeout = $browser.defer(function() {
19600
          timeout = null;
19601
          if (!input || input.value !== origValue) {
19602
            listener(ev);
19603
          }
19604
        });
19605
      }
19606
    };
19607
 
19608
    element.on('keydown', function(event) {
19609
      var key = event.keyCode;
19610
 
19611
      // ignore
19612
      //    command            modifiers                   arrows
19613
      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
19614
 
19615
      deferListener(event, this, this.value);
19616
    });
19617
 
19618
    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
19619
    if ($sniffer.hasEvent('paste')) {
19620
      element.on('paste cut', deferListener);
19621
    }
19622
  }
19623
 
19624
  // if user paste into input using mouse on older browser
19625
  // or form autocomplete on newer browser, we need "change" event to catch it
19626
  element.on('change', listener);
19627
 
19628
  ctrl.$render = function() {
19629
    element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19630
  };
19631
}
19632
 
19633
function weekParser(isoWeek, existingDate) {
19634
  if (isDate(isoWeek)) {
19635
    return isoWeek;
19636
  }
19637
 
19638
  if (isString(isoWeek)) {
19639
    WEEK_REGEXP.lastIndex = 0;
19640
    var parts = WEEK_REGEXP.exec(isoWeek);
19641
    if (parts) {
19642
      var year = +parts[1],
19643
          week = +parts[2],
19644
          hours = 0,
19645
          minutes = 0,
19646
          seconds = 0,
19647
          milliseconds = 0,
19648
          firstThurs = getFirstThursdayOfYear(year),
19649
          addDays = (week - 1) * 7;
19650
 
19651
      if (existingDate) {
19652
        hours = existingDate.getHours();
19653
        minutes = existingDate.getMinutes();
19654
        seconds = existingDate.getSeconds();
19655
        milliseconds = existingDate.getMilliseconds();
19656
      }
19657
 
19658
      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
19659
    }
19660
  }
19661
 
19662
  return NaN;
19663
}
19664
 
19665
function createDateParser(regexp, mapping) {
19666
  return function(iso, date) {
19667
    var parts, map;
19668
 
19669
    if (isDate(iso)) {
19670
      return iso;
19671
    }
19672
 
19673
    if (isString(iso)) {
19674
      // When a date is JSON'ified to wraps itself inside of an extra
19675
      // set of double quotes. This makes the date parsing code unable
19676
      // to match the date string and parse it as a date.
19677
      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
19678
        iso = iso.substring(1, iso.length - 1);
19679
      }
19680
      if (ISO_DATE_REGEXP.test(iso)) {
19681
        return new Date(iso);
19682
      }
19683
      regexp.lastIndex = 0;
19684
      parts = regexp.exec(iso);
19685
 
19686
      if (parts) {
19687
        parts.shift();
19688
        if (date) {
19689
          map = {
19690
            yyyy: date.getFullYear(),
19691
            MM: date.getMonth() + 1,
19692
            dd: date.getDate(),
19693
            HH: date.getHours(),
19694
            mm: date.getMinutes(),
19695
            ss: date.getSeconds(),
19696
            sss: date.getMilliseconds() / 1000
19697
          };
19698
        } else {
19699
          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
19700
        }
19701
 
19702
        forEach(parts, function(part, index) {
19703
          if (index < mapping.length) {
19704
            map[mapping[index]] = +part;
19705
          }
19706
        });
19707
        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
19708
      }
19709
    }
19710
 
19711
    return NaN;
19712
  };
19713
}
19714
 
19715
function createDateInputType(type, regexp, parseDate, format) {
19716
  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
19717
    badInputChecker(scope, element, attr, ctrl);
19718
    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19719
    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
19720
    var previousDate;
19721
 
19722
    ctrl.$$parserName = type;
19723
    ctrl.$parsers.push(function(value) {
19724
      if (ctrl.$isEmpty(value)) return null;
19725
      if (regexp.test(value)) {
19726
        // Note: We cannot read ctrl.$modelValue, as there might be a different
19727
        // parser/formatter in the processing chain so that the model
19728
        // contains some different data format!
19729
        var parsedDate = parseDate(value, previousDate);
19730
        if (timezone === 'UTC') {
19731
          parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
19732
        }
19733
        return parsedDate;
19734
      }
19735
      return undefined;
19736
    });
19737
 
19738
    ctrl.$formatters.push(function(value) {
19739
      if (value && !isDate(value)) {
19740
        throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19741
      }
19742
      if (isValidDate(value)) {
19743
        previousDate = value;
19744
        if (previousDate && timezone === 'UTC') {
19745
          var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
19746
          previousDate = new Date(previousDate.getTime() + timezoneOffset);
19747
        }
19748
        return $filter('date')(value, format, timezone);
19749
      } else {
19750
        previousDate = null;
19751
        return '';
19752
      }
19753
    });
19754
 
19755
    if (isDefined(attr.min) || attr.ngMin) {
19756
      var minVal;
19757
      ctrl.$validators.min = function(value) {
19758
        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19759
      };
19760
      attr.$observe('min', function(val) {
19761
        minVal = parseObservedDateValue(val);
19762
        ctrl.$validate();
19763
      });
19764
    }
19765
 
19766
    if (isDefined(attr.max) || attr.ngMax) {
19767
      var maxVal;
19768
      ctrl.$validators.max = function(value) {
19769
        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19770
      };
19771
      attr.$observe('max', function(val) {
19772
        maxVal = parseObservedDateValue(val);
19773
        ctrl.$validate();
19774
      });
19775
    }
19776
 
19777
    function isValidDate(value) {
19778
      // Invalid Date: getTime() returns NaN
19779
      return value && !(value.getTime && value.getTime() !== value.getTime());
19780
    }
19781
 
19782
    function parseObservedDateValue(val) {
19783
      return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
19784
    }
19785
  };
19786
}
19787
 
19788
function badInputChecker(scope, element, attr, ctrl) {
19789
  var node = element[0];
19790
  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
19791
  if (nativeValidation) {
19792
    ctrl.$parsers.push(function(value) {
19793
      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
19794
      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
19795
      // - also sets validity.badInput (should only be validity.typeMismatch).
19796
      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
19797
      // - can ignore this case as we can still read out the erroneous email...
19798
      return validity.badInput && !validity.typeMismatch ? undefined : value;
19799
    });
19800
  }
19801
}
19802
 
19803
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19804
  badInputChecker(scope, element, attr, ctrl);
19805
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19806
 
19807
  ctrl.$$parserName = 'number';
19808
  ctrl.$parsers.push(function(value) {
19809
    if (ctrl.$isEmpty(value))      return null;
19810
    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
19811
    return undefined;
19812
  });
19813
 
19814
  ctrl.$formatters.push(function(value) {
19815
    if (!ctrl.$isEmpty(value)) {
19816
      if (!isNumber(value)) {
19817
        throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
19818
      }
19819
      value = value.toString();
19820
    }
19821
    return value;
19822
  });
19823
 
19824
  if (attr.min || attr.ngMin) {
19825
    var minVal;
19826
    ctrl.$validators.min = function(value) {
19827
      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
19828
    };
19829
 
19830
    attr.$observe('min', function(val) {
19831
      if (isDefined(val) && !isNumber(val)) {
19832
        val = parseFloat(val, 10);
19833
      }
19834
      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
19835
      // TODO(matsko): implement validateLater to reduce number of validations
19836
      ctrl.$validate();
19837
    });
19838
  }
19839
 
19840
  if (attr.max || attr.ngMax) {
19841
    var maxVal;
19842
    ctrl.$validators.max = function(value) {
19843
      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
19844
    };
19845
 
19846
    attr.$observe('max', function(val) {
19847
      if (isDefined(val) && !isNumber(val)) {
19848
        val = parseFloat(val, 10);
19849
      }
19850
      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
19851
      // TODO(matsko): implement validateLater to reduce number of validations
19852
      ctrl.$validate();
19853
    });
19854
  }
19855
}
19856
 
19857
function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19858
  // Note: no badInputChecker here by purpose as `url` is only a validation
19859
  // in browsers, i.e. we can always read out input.value even if it is not valid!
19860
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19861
  stringBasedInputType(ctrl);
19862
 
19863
  ctrl.$$parserName = 'url';
19864
  ctrl.$validators.url = function(modelValue, viewValue) {
19865
    var value = modelValue || viewValue;
19866
    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
19867
  };
19868
}
19869
 
19870
function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19871
  // Note: no badInputChecker here by purpose as `url` is only a validation
19872
  // in browsers, i.e. we can always read out input.value even if it is not valid!
19873
  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19874
  stringBasedInputType(ctrl);
19875
 
19876
  ctrl.$$parserName = 'email';
19877
  ctrl.$validators.email = function(modelValue, viewValue) {
19878
    var value = modelValue || viewValue;
19879
    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
19880
  };
19881
}
19882
 
19883
function radioInputType(scope, element, attr, ctrl) {
19884
  // make the name unique, if not defined
19885
  if (isUndefined(attr.name)) {
19886
    element.attr('name', nextUid());
19887
  }
19888
 
19889
  var listener = function(ev) {
19890
    if (element[0].checked) {
19891
      ctrl.$setViewValue(attr.value, ev && ev.type);
19892
    }
19893
  };
19894
 
19895
  element.on('click', listener);
19896
 
19897
  ctrl.$render = function() {
19898
    var value = attr.value;
19899
    element[0].checked = (value == ctrl.$viewValue);
19900
  };
19901
 
19902
  attr.$observe('value', ctrl.$render);
19903
}
19904
 
19905
function parseConstantExpr($parse, context, name, expression, fallback) {
19906
  var parseFn;
19907
  if (isDefined(expression)) {
19908
    parseFn = $parse(expression);
19909
    if (!parseFn.constant) {
19910
      throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
19911
                                   '`{1}`.', name, expression);
19912
    }
19913
    return parseFn(context);
19914
  }
19915
  return fallback;
19916
}
19917
 
19918
function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
19919
  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
19920
  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
19921
 
19922
  var listener = function(ev) {
19923
    ctrl.$setViewValue(element[0].checked, ev && ev.type);
19924
  };
19925
 
19926
  element.on('click', listener);
19927
 
19928
  ctrl.$render = function() {
19929
    element[0].checked = ctrl.$viewValue;
19930
  };
19931
 
19932
  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
19933
  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
19934
  // it to a boolean.
19935
  ctrl.$isEmpty = function(value) {
19936
    return value === false;
19937
  };
19938
 
19939
  ctrl.$formatters.push(function(value) {
19940
    return equals(value, trueValue);
19941
  });
19942
 
19943
  ctrl.$parsers.push(function(value) {
19944
    return value ? trueValue : falseValue;
19945
  });
19946
}
19947
 
19948
 
19949
/**
19950
 * @ngdoc directive
19951
 * @name textarea
19952
 * @restrict E
19953
 *
19954
 * @description
19955
 * HTML textarea element control with angular data-binding. The data-binding and validation
19956
 * properties of this element are exactly the same as those of the
19957
 * {@link ng.directive:input input element}.
19958
 *
19959
 * @param {string} ngModel Assignable angular expression to data-bind to.
19960
 * @param {string=} name Property name of the form under which the control is published.
19961
 * @param {string=} required Sets `required` validation error key if the value is not entered.
19962
 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19963
 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19964
 *    `required` when you want to data-bind to the `required` attribute.
19965
 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19966
 *    minlength.
19967
 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19968
 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
19969
 *    length.
19970
 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19971
 *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19972
 *    patterns defined as scope expressions.
19973
 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19974
 *    interaction with the input element.
19975
 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
19976
 */
19977
 
19978
 
19979
/**
19980
 * @ngdoc directive
19981
 * @name input
19982
 * @restrict E
19983
 *
19984
 * @description
19985
 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
19986
 * input state control, and validation.
19987
 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
19988
 *
19989
 * <div class="alert alert-warning">
19990
 * **Note:** Not every feature offered is available for all input types.
19991
 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
19992
 * </div>
19993
 *
19994
 * @param {string} ngModel Assignable angular expression to data-bind to.
19995
 * @param {string=} name Property name of the form under which the control is published.
19996
 * @param {string=} required Sets `required` validation error key if the value is not entered.
19997
 * @param {boolean=} ngRequired Sets `required` attribute if set to true
19998
 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19999
 *    minlength.
20000
 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20001
 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20002
 *    length.
20003
 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20004
 *    RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20005
 *    patterns defined as scope expressions.
20006
 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20007
 *    interaction with the input element.
20008
 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20009
 *    This parameter is ignored for input[type=password] controls, which will never trim the
20010
 *    input.
20011
 *
20012
 * @example
20013
    <example name="input-directive" module="inputExample">
20014
      <file name="index.html">
20015
       <script>
20016
          angular.module('inputExample', [])
20017
            .controller('ExampleController', ['$scope', function($scope) {
20018
              $scope.user = {name: 'guest', last: 'visitor'};
20019
            }]);
20020
       </script>
20021
       <div ng-controller="ExampleController">
20022
         <form name="myForm">
20023
           User name: <input type="text" name="userName" ng-model="user.name" required>
20024
           <span class="error" ng-show="myForm.userName.$error.required">
20025
             Required!</span><br>
20026
           Last name: <input type="text" name="lastName" ng-model="user.last"
20027
             ng-minlength="3" ng-maxlength="10">
20028
           <span class="error" ng-show="myForm.lastName.$error.minlength">
20029
             Too short!</span>
20030
           <span class="error" ng-show="myForm.lastName.$error.maxlength">
20031
             Too long!</span><br>
20032
         </form>
20033
         <hr>
20034
         <tt>user = {{user}}</tt><br/>
20035
         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
20036
         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
20037
         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
20038
         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
20039
         <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20040
         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20041
         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
20042
         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
20043
       </div>
20044
      </file>
20045
      <file name="protractor.js" type="protractor">
20046
        var user = element(by.exactBinding('user'));
20047
        var userNameValid = element(by.binding('myForm.userName.$valid'));
20048
        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
20049
        var lastNameError = element(by.binding('myForm.lastName.$error'));
20050
        var formValid = element(by.binding('myForm.$valid'));
20051
        var userNameInput = element(by.model('user.name'));
20052
        var userLastInput = element(by.model('user.last'));
20053
 
20054
        it('should initialize to model', function() {
20055
          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
20056
          expect(userNameValid.getText()).toContain('true');
20057
          expect(formValid.getText()).toContain('true');
20058
        });
20059
 
20060
        it('should be invalid if empty when required', function() {
20061
          userNameInput.clear();
20062
          userNameInput.sendKeys('');
20063
 
20064
          expect(user.getText()).toContain('{"last":"visitor"}');
20065
          expect(userNameValid.getText()).toContain('false');
20066
          expect(formValid.getText()).toContain('false');
20067
        });
20068
 
20069
        it('should be valid if empty when min length is set', function() {
20070
          userLastInput.clear();
20071
          userLastInput.sendKeys('');
20072
 
20073
          expect(user.getText()).toContain('{"name":"guest","last":""}');
20074
          expect(lastNameValid.getText()).toContain('true');
20075
          expect(formValid.getText()).toContain('true');
20076
        });
20077
 
20078
        it('should be invalid if less than required min length', function() {
20079
          userLastInput.clear();
20080
          userLastInput.sendKeys('xx');
20081
 
20082
          expect(user.getText()).toContain('{"name":"guest"}');
20083
          expect(lastNameValid.getText()).toContain('false');
20084
          expect(lastNameError.getText()).toContain('minlength');
20085
          expect(formValid.getText()).toContain('false');
20086
        });
20087
 
20088
        it('should be invalid if longer than max length', function() {
20089
          userLastInput.clear();
20090
          userLastInput.sendKeys('some ridiculously long name');
20091
 
20092
          expect(user.getText()).toContain('{"name":"guest"}');
20093
          expect(lastNameValid.getText()).toContain('false');
20094
          expect(lastNameError.getText()).toContain('maxlength');
20095
          expect(formValid.getText()).toContain('false');
20096
        });
20097
      </file>
20098
    </example>
20099
 */
20100
var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
20101
    function($browser, $sniffer, $filter, $parse) {
20102
  return {
20103
    restrict: 'E',
20104
    require: ['?ngModel'],
20105
    link: {
20106
      pre: function(scope, element, attr, ctrls) {
20107
        if (ctrls[0]) {
20108
          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
20109
                                                              $browser, $filter, $parse);
20110
        }
20111
      }
20112
    }
20113
  };
20114
}];
20115
 
20116
var VALID_CLASS = 'ng-valid',
20117
    INVALID_CLASS = 'ng-invalid',
20118
    PRISTINE_CLASS = 'ng-pristine',
20119
    DIRTY_CLASS = 'ng-dirty',
20120
    UNTOUCHED_CLASS = 'ng-untouched',
20121
    TOUCHED_CLASS = 'ng-touched',
20122
    PENDING_CLASS = 'ng-pending';
20123
 
20124
/**
20125
 * @ngdoc type
20126
 * @name ngModel.NgModelController
20127
 *
20128
 * @property {string} $viewValue Actual string value in the view.
20129
 * @property {*} $modelValue The value in the model that the control is bound to.
20130
 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
20131
       the control reads value from the DOM. The functions are called in array order, each passing
20132
       its return value through to the next. The last return value is forwarded to the
20133
       {@link ngModel.NgModelController#$validators `$validators`} collection.
20134
 
20135
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
20136
`$viewValue`}.
20137
 
20138
Returning `undefined` from a parser means a parse error occurred. In that case,
20139
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
20140
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
20141
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
20142
 
20143
 *
20144
 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
20145
       the model value changes. The functions are called in reverse array order, each passing the value through to the
20146
       next. The last return value is used as the actual DOM value.
20147
       Used to format / convert values for display in the control.
20148
 * ```js
20149
 * function formatter(value) {
20150
 *   if (value) {
20151
 *     return value.toUpperCase();
20152
 *   }
20153
 * }
20154
 * ngModel.$formatters.push(formatter);
20155
 * ```
20156
 *
20157
 * @property {Object.<string, function>} $validators A collection of validators that are applied
20158
 *      whenever the model value changes. The key value within the object refers to the name of the
20159
 *      validator while the function refers to the validation operation. The validation operation is
20160
 *      provided with the model value as an argument and must return a true or false value depending
20161
 *      on the response of that validation.
20162
 *
20163
 * ```js
20164
 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
20165
 *   var value = modelValue || viewValue;
20166
 *   return /[0-9]+/.test(value) &&
20167
 *          /[a-z]+/.test(value) &&
20168
 *          /[A-Z]+/.test(value) &&
20169
 *          /\W+/.test(value);
20170
 * };
20171
 * ```
20172
 *
20173
 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
20174
 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
20175
 *      is expected to return a promise when it is run during the model validation process. Once the promise
20176
 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
20177
 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
20178
 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
20179
 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
20180
 *      will only run once all synchronous validators have passed.
20181
 *
20182
 * Please note that if $http is used then it is important that the server returns a success HTTP response code
20183
 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
20184
 *
20185
 * ```js
20186
 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
20187
 *   var value = modelValue || viewValue;
20188
 *
20189
 *   // Lookup user by username
20190
 *   return $http.get('/api/users/' + value).
20191
 *      then(function resolved() {
20192
 *        //username exists, this means validation fails
20193
 *        return $q.reject('exists');
20194
 *      }, function rejected() {
20195
 *        //username does not exist, therefore this validation passes
20196
 *        return true;
20197
 *      });
20198
 * };
20199
 * ```
20200
 *
20201
 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
20202
 *     view value has changed. It is called with no arguments, and its return value is ignored.
20203
 *     This can be used in place of additional $watches against the model value.
20204
 *
20205
 * @property {Object} $error An object hash with all failing validator ids as keys.
20206
 * @property {Object} $pending An object hash with all pending validator ids as keys.
20207
 *
20208
 * @property {boolean} $untouched True if control has not lost focus yet.
20209
 * @property {boolean} $touched True if control has lost focus.
20210
 * @property {boolean} $pristine True if user has not interacted with the control yet.
20211
 * @property {boolean} $dirty True if user has already interacted with the control.
20212
 * @property {boolean} $valid True if there is no error.
20213
 * @property {boolean} $invalid True if at least one error on the control.
20214
 * @property {string} $name The name attribute of the control.
20215
 *
20216
 * @description
20217
 *
20218
 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
20219
 * The controller contains services for data-binding, validation, CSS updates, and value formatting
20220
 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
20221
 * listening to DOM events.
20222
 * Such DOM related logic should be provided by other directives which make use of
20223
 * `NgModelController` for data-binding to control elements.
20224
 * Angular provides this DOM logic for most {@link input `input`} elements.
20225
 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
20226
 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
20227
 *
20228
 * @example
20229
 * ### Custom Control Example
20230
 * This example shows how to use `NgModelController` with a custom control to achieve
20231
 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
20232
 * collaborate together to achieve the desired result.
20233
 *
20234
 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
20235
 * contents be edited in place by the user.  This will not work on older browsers.
20236
 *
20237
 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
20238
 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
20239
 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
20240
 * that content using the `$sce` service.
20241
 *
20242
 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
20243
    <file name="style.css">
20244
      [contenteditable] {
20245
        border: 1px solid black;
20246
        background-color: white;
20247
        min-height: 20px;
20248
      }
20249
 
20250
      .ng-invalid {
20251
        border: 1px solid red;
20252
      }
20253
 
20254
    </file>
20255
    <file name="script.js">
20256
      angular.module('customControl', ['ngSanitize']).
20257
        directive('contenteditable', ['$sce', function($sce) {
20258
          return {
20259
            restrict: 'A', // only activate on element attribute
20260
            require: '?ngModel', // get a hold of NgModelController
20261
            link: function(scope, element, attrs, ngModel) {
20262
              if (!ngModel) return; // do nothing if no ng-model
20263
 
20264
              // Specify how UI should be updated
20265
              ngModel.$render = function() {
20266
                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
20267
              };
20268
 
20269
              // Listen for change events to enable binding
20270
              element.on('blur keyup change', function() {
20271
                scope.$evalAsync(read);
20272
              });
20273
              read(); // initialize
20274
 
20275
              // Write data to the model
20276
              function read() {
20277
                var html = element.html();
20278
                // When we clear the content editable the browser leaves a <br> behind
20279
                // If strip-br attribute is provided then we strip this out
20280
                if ( attrs.stripBr && html == '<br>' ) {
20281
                  html = '';
20282
                }
20283
                ngModel.$setViewValue(html);
20284
              }
20285
            }
20286
          };
20287
        }]);
20288
    </file>
20289
    <file name="index.html">
20290
      <form name="myForm">
20291
       <div contenteditable
20292
            name="myWidget" ng-model="userContent"
20293
            strip-br="true"
20294
            required>Change me!</div>
20295
        <span ng-show="myForm.myWidget.$error.required">Required!</span>
20296
       <hr>
20297
       <textarea ng-model="userContent"></textarea>
20298
      </form>
20299
    </file>
20300
    <file name="protractor.js" type="protractor">
20301
    it('should data-bind and become invalid', function() {
20302
      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
20303
        // SafariDriver can't handle contenteditable
20304
        // and Firefox driver can't clear contenteditables very well
20305
        return;
20306
      }
20307
      var contentEditable = element(by.css('[contenteditable]'));
20308
      var content = 'Change me!';
20309
 
20310
      expect(contentEditable.getText()).toEqual(content);
20311
 
20312
      contentEditable.clear();
20313
      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
20314
      expect(contentEditable.getText()).toEqual('');
20315
      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
20316
    });
20317
    </file>
20318
 * </example>
20319
 *
20320
 *
20321
 */
20322
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
20323
    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
20324
  this.$viewValue = Number.NaN;
20325
  this.$modelValue = Number.NaN;
20326
  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
20327
  this.$validators = {};
20328
  this.$asyncValidators = {};
20329
  this.$parsers = [];
20330
  this.$formatters = [];
20331
  this.$viewChangeListeners = [];
20332
  this.$untouched = true;
20333
  this.$touched = false;
20334
  this.$pristine = true;
20335
  this.$dirty = false;
20336
  this.$valid = true;
20337
  this.$invalid = false;
20338
  this.$error = {}; // keep invalid keys here
20339
  this.$$success = {}; // keep valid keys here
20340
  this.$pending = undefined; // keep pending keys here
20341
  this.$name = $interpolate($attr.name || '', false)($scope);
20342
 
20343
 
20344
  var parsedNgModel = $parse($attr.ngModel),
20345
      parsedNgModelAssign = parsedNgModel.assign,
20346
      ngModelGet = parsedNgModel,
20347
      ngModelSet = parsedNgModelAssign,
20348
      pendingDebounce = null,
20349
      ctrl = this;
20350
 
20351
  this.$$setOptions = function(options) {
20352
    ctrl.$options = options;
20353
    if (options && options.getterSetter) {
20354
      var invokeModelGetter = $parse($attr.ngModel + '()'),
20355
          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
20356
 
20357
      ngModelGet = function($scope) {
20358
        var modelValue = parsedNgModel($scope);
20359
        if (isFunction(modelValue)) {
20360
          modelValue = invokeModelGetter($scope);
20361
        }
20362
        return modelValue;
20363
      };
20364
      ngModelSet = function($scope, newValue) {
20365
        if (isFunction(parsedNgModel($scope))) {
20366
          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
20367
        } else {
20368
          parsedNgModelAssign($scope, ctrl.$modelValue);
20369
        }
20370
      };
20371
    } else if (!parsedNgModel.assign) {
20372
      throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
20373
          $attr.ngModel, startingTag($element));
20374
    }
20375
  };
20376
 
20377
  /**
20378
   * @ngdoc method
20379
   * @name ngModel.NgModelController#$render
20380
   *
20381
   * @description
20382
   * Called when the view needs to be updated. It is expected that the user of the ng-model
20383
   * directive will implement this method.
20384
   *
20385
   * The `$render()` method is invoked in the following situations:
20386
   *
20387
   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
20388
   *   committed value then `$render()` is called to update the input control.
20389
   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
20390
   *   the `$viewValue` are different to last time.
20391
   *
20392
   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
20393
   * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`
20394
   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
20395
   * invoked if you only change a property on the objects.
20396
   */
20397
  this.$render = noop;
20398
 
20399
  /**
20400
   * @ngdoc method
20401
   * @name ngModel.NgModelController#$isEmpty
20402
   *
20403
   * @description
20404
   * This is called when we need to determine if the value of an input is empty.
20405
   *
20406
   * For instance, the required directive does this to work out if the input has data or not.
20407
   *
20408
   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
20409
   *
20410
   * You can override this for input directives whose concept of being empty is different to the
20411
   * default. The `checkboxInputType` directive does this because in its case a value of `false`
20412
   * implies empty.
20413
   *
20414
   * @param {*} value The value of the input to check for emptiness.
20415
   * @returns {boolean} True if `value` is "empty".
20416
   */
20417
  this.$isEmpty = function(value) {
20418
    return isUndefined(value) || value === '' || value === null || value !== value;
20419
  };
20420
 
20421
  var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
20422
      currentValidationRunId = 0;
20423
 
20424
  /**
20425
   * @ngdoc method
20426
   * @name ngModel.NgModelController#$setValidity
20427
   *
20428
   * @description
20429
   * Change the validity state, and notify the form.
20430
   *
20431
   * This method can be called within $parsers/$formatters or a custom validation implementation.
20432
   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
20433
   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
20434
   *
20435
   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
20436
   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
20437
   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
20438
   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
20439
   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
20440
   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
20441
   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
20442
   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
20443
   *                          Skipped is used by Angular when validators do not run because of parse errors and
20444
   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
20445
   */
20446
  addSetValidityMethod({
20447
    ctrl: this,
20448
    $element: $element,
20449
    set: function(object, property) {
20450
      object[property] = true;
20451
    },
20452
    unset: function(object, property) {
20453
      delete object[property];
20454
    },
20455
    parentForm: parentForm,
20456
    $animate: $animate
20457
  });
20458
 
20459
  /**
20460
   * @ngdoc method
20461
   * @name ngModel.NgModelController#$setPristine
20462
   *
20463
   * @description
20464
   * Sets the control to its pristine state.
20465
   *
20466
   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
20467
   * state (`ng-pristine` class). A model is considered to be pristine when the control
20468
   * has not been changed from when first compiled.
20469
   */
20470
  this.$setPristine = function() {
20471
    ctrl.$dirty = false;
20472
    ctrl.$pristine = true;
20473
    $animate.removeClass($element, DIRTY_CLASS);
20474
    $animate.addClass($element, PRISTINE_CLASS);
20475
  };
20476
 
20477
  /**
20478
   * @ngdoc method
20479
   * @name ngModel.NgModelController#$setDirty
20480
   *
20481
   * @description
20482
   * Sets the control to its dirty state.
20483
   *
20484
   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
20485
   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
20486
   * from when first compiled.
20487
   */
20488
  this.$setDirty = function() {
20489
    ctrl.$dirty = true;
20490
    ctrl.$pristine = false;
20491
    $animate.removeClass($element, PRISTINE_CLASS);
20492
    $animate.addClass($element, DIRTY_CLASS);
20493
    parentForm.$setDirty();
20494
  };
20495
 
20496
  /**
20497
   * @ngdoc method
20498
   * @name ngModel.NgModelController#$setUntouched
20499
   *
20500
   * @description
20501
   * Sets the control to its untouched state.
20502
   *
20503
   * This method can be called to remove the `ng-touched` class and set the control to its
20504
   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
20505
   * by default, however this function can be used to restore that state if the model has
20506
   * already been touched by the user.
20507
   */
20508
  this.$setUntouched = function() {
20509
    ctrl.$touched = false;
20510
    ctrl.$untouched = true;
20511
    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
20512
  };
20513
 
20514
  /**
20515
   * @ngdoc method
20516
   * @name ngModel.NgModelController#$setTouched
20517
   *
20518
   * @description
20519
   * Sets the control to its touched state.
20520
   *
20521
   * This method can be called to remove the `ng-untouched` class and set the control to its
20522
   * touched state (`ng-touched` class). A model is considered to be touched when the user has
20523
   * first focused the control element and then shifted focus away from the control (blur event).
20524
   */
20525
  this.$setTouched = function() {
20526
    ctrl.$touched = true;
20527
    ctrl.$untouched = false;
20528
    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
20529
  };
20530
 
20531
  /**
20532
   * @ngdoc method
20533
   * @name ngModel.NgModelController#$rollbackViewValue
20534
   *
20535
   * @description
20536
   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
20537
   * which may be caused by a pending debounced event or because the input is waiting for a some
20538
   * future event.
20539
   *
20540
   * If you have an input that uses `ng-model-options` to set up debounced events or events such
20541
   * as blur you can have a situation where there is a period when the `$viewValue`
20542
   * is out of synch with the ngModel's `$modelValue`.
20543
   *
20544
   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
20545
   * programmatically before these debounced/future events have resolved/occurred, because Angular's
20546
   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
20547
   *
20548
   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
20549
   * input which may have such events pending. This is important in order to make sure that the
20550
   * input field will be updated with the new model value and any pending operations are cancelled.
20551
   *
20552
   * <example name="ng-model-cancel-update" module="cancel-update-example">
20553
   *   <file name="app.js">
20554
   *     angular.module('cancel-update-example', [])
20555
   *
20556
   *     .controller('CancelUpdateController', ['$scope', function($scope) {
20557
   *       $scope.resetWithCancel = function(e) {
20558
   *         if (e.keyCode == 27) {
20559
   *           $scope.myForm.myInput1.$rollbackViewValue();
20560
   *           $scope.myValue = '';
20561
   *         }
20562
   *       };
20563
   *       $scope.resetWithoutCancel = function(e) {
20564
   *         if (e.keyCode == 27) {
20565
   *           $scope.myValue = '';
20566
   *         }
20567
   *       };
20568
   *     }]);
20569
   *   </file>
20570
   *   <file name="index.html">
20571
   *     <div ng-controller="CancelUpdateController">
20572
   *       <p>Try typing something in each input.  See that the model only updates when you
20573
   *          blur off the input.
20574
   *        </p>
20575
   *        <p>Now see what happens if you start typing then press the Escape key</p>
20576
   *
20577
   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
20578
   *         <p>With $rollbackViewValue()</p>
20579
   *         <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/>
20580
   *         myValue: "{{ myValue }}"
20581
   *
20582
   *         <p>Without $rollbackViewValue()</p>
20583
   *         <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/>
20584
   *         myValue: "{{ myValue }}"
20585
   *       </form>
20586
   *     </div>
20587
   *   </file>
20588
   * </example>
20589
   */
20590
  this.$rollbackViewValue = function() {
20591
    $timeout.cancel(pendingDebounce);
20592
    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
20593
    ctrl.$render();
20594
  };
20595
 
20596
  /**
20597
   * @ngdoc method
20598
   * @name ngModel.NgModelController#$validate
20599
   *
20600
   * @description
20601
   * Runs each of the registered validators (first synchronous validators and then
20602
   * asynchronous validators).
20603
   * If the validity changes to invalid, the model will be set to `undefined`,
20604
   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
20605
   * If the validity changes to valid, it will set the model to the last available valid
20606
   * modelValue, i.e. either the last parsed value or the last value set from the scope.
20607
   */
20608
  this.$validate = function() {
20609
    // ignore $validate before model is initialized
20610
    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20611
      return;
20612
    }
20613
 
20614
    var viewValue = ctrl.$$lastCommittedViewValue;
20615
    // Note: we use the $$rawModelValue as $modelValue might have been
20616
    // set to undefined during a view -> model update that found validation
20617
    // errors. We can't parse the view here, since that could change
20618
    // the model although neither viewValue nor the model on the scope changed
20619
    var modelValue = ctrl.$$rawModelValue;
20620
 
20621
    // Check if the there's a parse error, so we don't unset it accidentially
20622
    var parserName = ctrl.$$parserName || 'parse';
20623
    var parserValid = ctrl.$error[parserName] ? false : undefined;
20624
 
20625
    var prevValid = ctrl.$valid;
20626
    var prevModelValue = ctrl.$modelValue;
20627
 
20628
    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20629
 
20630
    ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
20631
      // If there was no change in validity, don't update the model
20632
      // This prevents changing an invalid modelValue to undefined
20633
      if (!allowInvalid && prevValid !== allValid) {
20634
        // Note: Don't check ctrl.$valid here, as we could have
20635
        // external validators (e.g. calculated on the server),
20636
        // that just call $setValidity and need the model value
20637
        // to calculate their validity.
20638
        ctrl.$modelValue = allValid ? modelValue : undefined;
20639
 
20640
        if (ctrl.$modelValue !== prevModelValue) {
20641
          ctrl.$$writeModelToScope();
20642
        }
20643
      }
20644
    });
20645
 
20646
  };
20647
 
20648
  this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) {
20649
    currentValidationRunId++;
20650
    var localValidationRunId = currentValidationRunId;
20651
 
20652
    // check parser error
20653
    if (!processParseErrors(parseValid)) {
20654
      validationDone(false);
20655
      return;
20656
    }
20657
    if (!processSyncValidators()) {
20658
      validationDone(false);
20659
      return;
20660
    }
20661
    processAsyncValidators();
20662
 
20663
    function processParseErrors(parseValid) {
20664
      var errorKey = ctrl.$$parserName || 'parse';
20665
      if (parseValid === undefined) {
20666
        setValidity(errorKey, null);
20667
      } else {
20668
        setValidity(errorKey, parseValid);
20669
        if (!parseValid) {
20670
          forEach(ctrl.$validators, function(v, name) {
20671
            setValidity(name, null);
20672
          });
20673
          forEach(ctrl.$asyncValidators, function(v, name) {
20674
            setValidity(name, null);
20675
          });
20676
          return false;
20677
        }
20678
      }
20679
      return true;
20680
    }
20681
 
20682
    function processSyncValidators() {
20683
      var syncValidatorsValid = true;
20684
      forEach(ctrl.$validators, function(validator, name) {
20685
        var result = validator(modelValue, viewValue);
20686
        syncValidatorsValid = syncValidatorsValid && result;
20687
        setValidity(name, result);
20688
      });
20689
      if (!syncValidatorsValid) {
20690
        forEach(ctrl.$asyncValidators, function(v, name) {
20691
          setValidity(name, null);
20692
        });
20693
        return false;
20694
      }
20695
      return true;
20696
    }
20697
 
20698
    function processAsyncValidators() {
20699
      var validatorPromises = [];
20700
      var allValid = true;
20701
      forEach(ctrl.$asyncValidators, function(validator, name) {
20702
        var promise = validator(modelValue, viewValue);
20703
        if (!isPromiseLike(promise)) {
20704
          throw $ngModelMinErr("$asyncValidators",
20705
            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
20706
        }
20707
        setValidity(name, undefined);
20708
        validatorPromises.push(promise.then(function() {
20709
          setValidity(name, true);
20710
        }, function(error) {
20711
          allValid = false;
20712
          setValidity(name, false);
20713
        }));
20714
      });
20715
      if (!validatorPromises.length) {
20716
        validationDone(true);
20717
      } else {
20718
        $q.all(validatorPromises).then(function() {
20719
          validationDone(allValid);
20720
        }, noop);
20721
      }
20722
    }
20723
 
20724
    function setValidity(name, isValid) {
20725
      if (localValidationRunId === currentValidationRunId) {
20726
        ctrl.$setValidity(name, isValid);
20727
      }
20728
    }
20729
 
20730
    function validationDone(allValid) {
20731
      if (localValidationRunId === currentValidationRunId) {
20732
 
20733
        doneCallback(allValid);
20734
      }
20735
    }
20736
  };
20737
 
20738
  /**
20739
   * @ngdoc method
20740
   * @name ngModel.NgModelController#$commitViewValue
20741
   *
20742
   * @description
20743
   * Commit a pending update to the `$modelValue`.
20744
   *
20745
   * Updates may be pending by a debounced event or because the input is waiting for a some future
20746
   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
20747
   * usually handles calling this in response to input events.
20748
   */
20749
  this.$commitViewValue = function() {
20750
    var viewValue = ctrl.$viewValue;
20751
 
20752
    $timeout.cancel(pendingDebounce);
20753
 
20754
    // If the view value has not changed then we should just exit, except in the case where there is
20755
    // a native validator on the element. In this case the validation state may have changed even though
20756
    // the viewValue has stayed empty.
20757
    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
20758
      return;
20759
    }
20760
    ctrl.$$lastCommittedViewValue = viewValue;
20761
 
20762
    // change to dirty
20763
    if (ctrl.$pristine) {
20764
      this.$setDirty();
20765
    }
20766
    this.$$parseAndValidate();
20767
  };
20768
 
20769
  this.$$parseAndValidate = function() {
20770
    var viewValue = ctrl.$$lastCommittedViewValue;
20771
    var modelValue = viewValue;
20772
    var parserValid = isUndefined(modelValue) ? undefined : true;
20773
 
20774
    if (parserValid) {
20775
      for (var i = 0; i < ctrl.$parsers.length; i++) {
20776
        modelValue = ctrl.$parsers[i](modelValue);
20777
        if (isUndefined(modelValue)) {
20778
          parserValid = false;
20779
          break;
20780
        }
20781
      }
20782
    }
20783
    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20784
      // ctrl.$modelValue has not been touched yet...
20785
      ctrl.$modelValue = ngModelGet($scope);
20786
    }
20787
    var prevModelValue = ctrl.$modelValue;
20788
    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20789
    ctrl.$$rawModelValue = modelValue;
20790
 
20791
    if (allowInvalid) {
20792
      ctrl.$modelValue = modelValue;
20793
      writeToModelIfNeeded();
20794
    }
20795
 
20796
    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
20797
    // This can happen if e.g. $setViewValue is called from inside a parser
20798
    ctrl.$$runValidators(parserValid, modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
20799
      if (!allowInvalid) {
20800
        // Note: Don't check ctrl.$valid here, as we could have
20801
        // external validators (e.g. calculated on the server),
20802
        // that just call $setValidity and need the model value
20803
        // to calculate their validity.
20804
        ctrl.$modelValue = allValid ? modelValue : undefined;
20805
        writeToModelIfNeeded();
20806
      }
20807
    });
20808
 
20809
    function writeToModelIfNeeded() {
20810
      if (ctrl.$modelValue !== prevModelValue) {
20811
        ctrl.$$writeModelToScope();
20812
      }
20813
    }
20814
  };
20815
 
20816
  this.$$writeModelToScope = function() {
20817
    ngModelSet($scope, ctrl.$modelValue);
20818
    forEach(ctrl.$viewChangeListeners, function(listener) {
20819
      try {
20820
        listener();
20821
      } catch (e) {
20822
        $exceptionHandler(e);
20823
      }
20824
    });
20825
  };
20826
 
20827
  /**
20828
   * @ngdoc method
20829
   * @name ngModel.NgModelController#$setViewValue
20830
   *
20831
   * @description
20832
   * Update the view value.
20833
   *
20834
   * This method should be called when an input directive want to change the view value; typically,
20835
   * this is done from within a DOM event handler.
20836
   *
20837
   * For example {@link ng.directive:input input} calls it when the value of the input changes and
20838
   * {@link ng.directive:select select} calls it when an option is selected.
20839
   *
20840
   * If the new `value` is an object (rather than a string or a number), we should make a copy of the
20841
   * object before passing it to `$setViewValue`.  This is because `ngModel` does not perform a deep
20842
   * watch of objects, it only looks for a change of identity. If you only change the property of
20843
   * the object then ngModel will not realise that the object has changed and will not invoke the
20844
   * `$parsers` and `$validators` pipelines.
20845
   *
20846
   * For this reason, you should not change properties of the copy once it has been passed to
20847
   * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
20848
   *
20849
   * When this method is called, the new `value` will be staged for committing through the `$parsers`
20850
   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
20851
   * value sent directly for processing, finally to be applied to `$modelValue` and then the
20852
   * **expression** specified in the `ng-model` attribute.
20853
   *
20854
   * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
20855
   *
20856
   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
20857
   * and the `default` trigger is not listed, all those actions will remain pending until one of the
20858
   * `updateOn` events is triggered on the DOM element.
20859
   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
20860
   * directive is used with a custom debounce for this particular event.
20861
   *
20862
   * Note that calling this function does not trigger a `$digest`.
20863
   *
20864
   * @param {string} value Value from the view.
20865
   * @param {string} trigger Event that triggered the update.
20866
   */
20867
  this.$setViewValue = function(value, trigger) {
20868
    ctrl.$viewValue = value;
20869
    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
20870
      ctrl.$$debounceViewValueCommit(trigger);
20871
    }
20872
  };
20873
 
20874
  this.$$debounceViewValueCommit = function(trigger) {
20875
    var debounceDelay = 0,
20876
        options = ctrl.$options,
20877
        debounce;
20878
 
20879
    if (options && isDefined(options.debounce)) {
20880
      debounce = options.debounce;
20881
      if (isNumber(debounce)) {
20882
        debounceDelay = debounce;
20883
      } else if (isNumber(debounce[trigger])) {
20884
        debounceDelay = debounce[trigger];
20885
      } else if (isNumber(debounce['default'])) {
20886
        debounceDelay = debounce['default'];
20887
      }
20888
    }
20889
 
20890
    $timeout.cancel(pendingDebounce);
20891
    if (debounceDelay) {
20892
      pendingDebounce = $timeout(function() {
20893
        ctrl.$commitViewValue();
20894
      }, debounceDelay);
20895
    } else if ($rootScope.$$phase) {
20896
      ctrl.$commitViewValue();
20897
    } else {
20898
      $scope.$apply(function() {
20899
        ctrl.$commitViewValue();
20900
      });
20901
    }
20902
  };
20903
 
20904
  // model -> value
20905
  // Note: we cannot use a normal scope.$watch as we want to detect the following:
20906
  // 1. scope value is 'a'
20907
  // 2. user enters 'b'
20908
  // 3. ng-change kicks in and reverts scope value to 'a'
20909
  //    -> scope value did not change since the last digest as
20910
  //       ng-change executes in apply phase
20911
  // 4. view should be changed back to 'a'
20912
  $scope.$watch(function ngModelWatch() {
20913
    var modelValue = ngModelGet($scope);
20914
 
20915
    // if scope model value and ngModel value are out of sync
20916
    // TODO(perf): why not move this to the action fn?
20917
    if (modelValue !== ctrl.$modelValue) {
20918
      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
20919
 
20920
      var formatters = ctrl.$formatters,
20921
          idx = formatters.length;
20922
 
20923
      var viewValue = modelValue;
20924
      while (idx--) {
20925
        viewValue = formatters[idx](viewValue);
20926
      }
20927
      if (ctrl.$viewValue !== viewValue) {
20928
        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
20929
        ctrl.$render();
20930
 
20931
        ctrl.$$runValidators(undefined, modelValue, viewValue, noop);
20932
      }
20933
    }
20934
 
20935
    return modelValue;
20936
  });
20937
}];
20938
 
20939
 
20940
/**
20941
 * @ngdoc directive
20942
 * @name ngModel
20943
 *
20944
 * @element input
20945
 * @priority 1
20946
 *
20947
 * @description
20948
 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
20949
 * property on the scope using {@link ngModel.NgModelController NgModelController},
20950
 * which is created and exposed by this directive.
20951
 *
20952
 * `ngModel` is responsible for:
20953
 *
20954
 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
20955
 *   require.
20956
 * - Providing validation behavior (i.e. required, number, email, url).
20957
 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
20958
 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
20959
 * - Registering the control with its parent {@link ng.directive:form form}.
20960
 *
20961
 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
20962
 * current scope. If the property doesn't already exist on this scope, it will be created
20963
 * implicitly and added to the scope.
20964
 *
20965
 * For best practices on using `ngModel`, see:
20966
 *
20967
 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
20968
 *
20969
 * For basic examples, how to use `ngModel`, see:
20970
 *
20971
 *  - {@link ng.directive:input input}
20972
 *    - {@link input[text] text}
20973
 *    - {@link input[checkbox] checkbox}
20974
 *    - {@link input[radio] radio}
20975
 *    - {@link input[number] number}
20976
 *    - {@link input[email] email}
20977
 *    - {@link input[url] url}
20978
 *    - {@link input[date] date}
20979
 *    - {@link input[datetime-local] datetime-local}
20980
 *    - {@link input[time] time}
20981
 *    - {@link input[month] month}
20982
 *    - {@link input[week] week}
20983
 *  - {@link ng.directive:select select}
20984
 *  - {@link ng.directive:textarea textarea}
20985
 *
20986
 * # CSS classes
20987
 * The following CSS classes are added and removed on the associated input/select/textarea element
20988
 * depending on the validity of the model.
20989
 *
20990
 *  - `ng-valid`: the model is valid
20991
 *  - `ng-invalid`: the model is invalid
20992
 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
20993
 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
20994
 *  - `ng-pristine`: the control hasn't been interacted with yet
20995
 *  - `ng-dirty`: the control has been interacted with
20996
 *  - `ng-touched`: the control has been blurred
20997
 *  - `ng-untouched`: the control hasn't been blurred
20998
 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
20999
 *
21000
 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
21001
 *
21002
 * ## Animation Hooks
21003
 *
21004
 * Animations within models are triggered when any of the associated CSS classes are added and removed
21005
 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
21006
 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
21007
 * The animations that are triggered within ngModel are similar to how they work in ngClass and
21008
 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
21009
 *
21010
 * The following example shows a simple way to utilize CSS transitions to style an input element
21011
 * that has been rendered as invalid after it has been validated:
21012
 *
21013
 * <pre>
21014
 * //be sure to include ngAnimate as a module to hook into more
21015
 * //advanced animations
21016
 * .my-input {
21017
 *   transition:0.5s linear all;
21018
 *   background: white;
21019
 * }
21020
 * .my-input.ng-invalid {
21021
 *   background: red;
21022
 *   color:white;
21023
 * }
21024
 * </pre>
21025
 *
21026
 * @example
21027
 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
21028
     <file name="index.html">
21029
       <script>
21030
        angular.module('inputExample', [])
21031
          .controller('ExampleController', ['$scope', function($scope) {
21032
            $scope.val = '1';
21033
          }]);
21034
       </script>
21035
       <style>
21036
         .my-input {
21037
           -webkit-transition:all linear 0.5s;
21038
           transition:all linear 0.5s;
21039
           background: transparent;
21040
         }
21041
         .my-input.ng-invalid {
21042
           color:white;
21043
           background: red;
21044
         }
21045
       </style>
21046
       Update input to see transitions when valid/invalid.
21047
       Integer is a valid value.
21048
       <form name="testForm" ng-controller="ExampleController">
21049
         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
21050
       </form>
21051
     </file>
21052
 * </example>
21053
 *
21054
 * ## Binding to a getter/setter
21055
 *
21056
 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
21057
 * function that returns a representation of the model when called with zero arguments, and sets
21058
 * the internal state of a model when called with an argument. It's sometimes useful to use this
21059
 * for models that have an internal representation that's different than what the model exposes
21060
 * to the view.
21061
 *
21062
 * <div class="alert alert-success">
21063
 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
21064
 * frequently than other parts of your code.
21065
 * </div>
21066
 *
21067
 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
21068
 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
21069
 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
21070
 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
21071
 *
21072
 * The following example shows how to use `ngModel` with a getter/setter:
21073
 *
21074
 * @example
21075
 * <example name="ngModel-getter-setter" module="getterSetterExample">
21076
     <file name="index.html">
21077
       <div ng-controller="ExampleController">
21078
         <form name="userForm">
21079
           Name:
21080
           <input type="text" name="userName"
21081
                  ng-model="user.name"
21082
                  ng-model-options="{ getterSetter: true }" />
21083
         </form>
21084
         <pre>user.name = <span ng-bind="user.name()"></span></pre>
21085
       </div>
21086
     </file>
21087
     <file name="app.js">
21088
       angular.module('getterSetterExample', [])
21089
         .controller('ExampleController', ['$scope', function($scope) {
21090
           var _name = 'Brian';
21091
           $scope.user = {
21092
             name: function(newName) {
21093
               if (angular.isDefined(newName)) {
21094
                 _name = newName;
21095
               }
21096
               return _name;
21097
             }
21098
           };
21099
         }]);
21100
     </file>
21101
 * </example>
21102
 */
21103
var ngModelDirective = ['$rootScope', function($rootScope) {
21104
  return {
21105
    restrict: 'A',
21106
    require: ['ngModel', '^?form', '^?ngModelOptions'],
21107
    controller: NgModelController,
21108
    // Prelink needs to run before any input directive
21109
    // so that we can set the NgModelOptions in NgModelController
21110
    // before anyone else uses it.
21111
    priority: 1,
21112
    compile: function ngModelCompile(element) {
21113
      // Setup initial state of the control
21114
      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
21115
 
21116
      return {
21117
        pre: function ngModelPreLink(scope, element, attr, ctrls) {
21118
          var modelCtrl = ctrls[0],
21119
              formCtrl = ctrls[1] || nullFormCtrl;
21120
 
21121
          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
21122
 
21123
          // notify others, especially parent forms
21124
          formCtrl.$addControl(modelCtrl);
21125
 
21126
          attr.$observe('name', function(newValue) {
21127
            if (modelCtrl.$name !== newValue) {
21128
              formCtrl.$$renameControl(modelCtrl, newValue);
21129
            }
21130
          });
21131
 
21132
          scope.$on('$destroy', function() {
21133
            formCtrl.$removeControl(modelCtrl);
21134
          });
21135
        },
21136
        post: function ngModelPostLink(scope, element, attr, ctrls) {
21137
          var modelCtrl = ctrls[0];
21138
          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
21139
            element.on(modelCtrl.$options.updateOn, function(ev) {
21140
              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
21141
            });
21142
          }
21143
 
21144
          element.on('blur', function(ev) {
21145
            if (modelCtrl.$touched) return;
21146
 
21147
            if ($rootScope.$$phase) {
21148
              scope.$evalAsync(modelCtrl.$setTouched);
21149
            } else {
21150
              scope.$apply(modelCtrl.$setTouched);
21151
            }
21152
          });
21153
        }
21154
      };
21155
    }
21156
  };
21157
}];
21158
 
21159
 
21160
/**
21161
 * @ngdoc directive
21162
 * @name ngChange
21163
 *
21164
 * @description
21165
 * Evaluate the given expression when the user changes the input.
21166
 * The expression is evaluated immediately, unlike the JavaScript onchange event
21167
 * which only triggers at the end of a change (usually, when the user leaves the
21168
 * form element or presses the return key).
21169
 *
21170
 * The `ngChange` expression is only evaluated when a change in the input value causes
21171
 * a new value to be committed to the model.
21172
 *
21173
 * It will not be evaluated:
21174
 * * if the value returned from the `$parsers` transformation pipeline has not changed
21175
 * * if the input has continued to be invalid since the model will stay `null`
21176
 * * if the model is changed programmatically and not by a change to the input value
21177
 *
21178
 *
21179
 * Note, this directive requires `ngModel` to be present.
21180
 *
21181
 * @element input
21182
 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
21183
 * in input value.
21184
 *
21185
 * @example
21186
 * <example name="ngChange-directive" module="changeExample">
21187
 *   <file name="index.html">
21188
 *     <script>
21189
 *       angular.module('changeExample', [])
21190
 *         .controller('ExampleController', ['$scope', function($scope) {
21191
 *           $scope.counter = 0;
21192
 *           $scope.change = function() {
21193
 *             $scope.counter++;
21194
 *           };
21195
 *         }]);
21196
 *     </script>
21197
 *     <div ng-controller="ExampleController">
21198
 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
21199
 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
21200
 *       <label for="ng-change-example2">Confirmed</label><br />
21201
 *       <tt>debug = {{confirmed}}</tt><br/>
21202
 *       <tt>counter = {{counter}}</tt><br/>
21203
 *     </div>
21204
 *   </file>
21205
 *   <file name="protractor.js" type="protractor">
21206
 *     var counter = element(by.binding('counter'));
21207
 *     var debug = element(by.binding('confirmed'));
21208
 *
21209
 *     it('should evaluate the expression if changing from view', function() {
21210
 *       expect(counter.getText()).toContain('0');
21211
 *
21212
 *       element(by.id('ng-change-example1')).click();
21213
 *
21214
 *       expect(counter.getText()).toContain('1');
21215
 *       expect(debug.getText()).toContain('true');
21216
 *     });
21217
 *
21218
 *     it('should not evaluate the expression if changing from model', function() {
21219
 *       element(by.id('ng-change-example2')).click();
21220
 
21221
 *       expect(counter.getText()).toContain('0');
21222
 *       expect(debug.getText()).toContain('true');
21223
 *     });
21224
 *   </file>
21225
 * </example>
21226
 */
21227
var ngChangeDirective = valueFn({
21228
  restrict: 'A',
21229
  require: 'ngModel',
21230
  link: function(scope, element, attr, ctrl) {
21231
    ctrl.$viewChangeListeners.push(function() {
21232
      scope.$eval(attr.ngChange);
21233
    });
21234
  }
21235
});
21236
 
21237
 
21238
var requiredDirective = function() {
21239
  return {
21240
    restrict: 'A',
21241
    require: '?ngModel',
21242
    link: function(scope, elm, attr, ctrl) {
21243
      if (!ctrl) return;
21244
      attr.required = true; // force truthy in case we are on non input element
21245
 
21246
      ctrl.$validators.required = function(modelValue, viewValue) {
21247
        return !attr.required || !ctrl.$isEmpty(viewValue);
21248
      };
21249
 
21250
      attr.$observe('required', function() {
21251
        ctrl.$validate();
21252
      });
21253
    }
21254
  };
21255
};
21256
 
21257
 
21258
var patternDirective = function() {
21259
  return {
21260
    restrict: 'A',
21261
    require: '?ngModel',
21262
    link: function(scope, elm, attr, ctrl) {
21263
      if (!ctrl) return;
21264
 
21265
      var regexp, patternExp = attr.ngPattern || attr.pattern;
21266
      attr.$observe('pattern', function(regex) {
21267
        if (isString(regex) && regex.length > 0) {
21268
          regex = new RegExp('^' + regex + '$');
21269
        }
21270
 
21271
        if (regex && !regex.test) {
21272
          throw minErr('ngPattern')('noregexp',
21273
            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
21274
            regex, startingTag(elm));
21275
        }
21276
 
21277
        regexp = regex || undefined;
21278
        ctrl.$validate();
21279
      });
21280
 
21281
      ctrl.$validators.pattern = function(value) {
21282
        return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
21283
      };
21284
    }
21285
  };
21286
};
21287
 
21288
 
21289
var maxlengthDirective = function() {
21290
  return {
21291
    restrict: 'A',
21292
    require: '?ngModel',
21293
    link: function(scope, elm, attr, ctrl) {
21294
      if (!ctrl) return;
21295
 
21296
      var maxlength = -1;
21297
      attr.$observe('maxlength', function(value) {
21298
        var intVal = int(value);
21299
        maxlength = isNaN(intVal) ? -1 : intVal;
21300
        ctrl.$validate();
21301
      });
21302
      ctrl.$validators.maxlength = function(modelValue, viewValue) {
21303
        return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength);
21304
      };
21305
    }
21306
  };
21307
};
21308
 
21309
var minlengthDirective = function() {
21310
  return {
21311
    restrict: 'A',
21312
    require: '?ngModel',
21313
    link: function(scope, elm, attr, ctrl) {
21314
      if (!ctrl) return;
21315
 
21316
      var minlength = 0;
21317
      attr.$observe('minlength', function(value) {
21318
        minlength = int(value) || 0;
21319
        ctrl.$validate();
21320
      });
21321
      ctrl.$validators.minlength = function(modelValue, viewValue) {
21322
        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
21323
      };
21324
    }
21325
  };
21326
};
21327
 
21328
 
21329
/**
21330
 * @ngdoc directive
21331
 * @name ngList
21332
 *
21333
 * @description
21334
 * Text input that converts between a delimited string and an array of strings. The default
21335
 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
21336
 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
21337
 *
21338
 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
21339
 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
21340
 *   list item is respected. This implies that the user of the directive is responsible for
21341
 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
21342
 *   tab or newline character.
21343
 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
21344
 *   when joining the list items back together) and whitespace around each list item is stripped
21345
 *   before it is added to the model.
21346
 *
21347
 * ### Example with Validation
21348
 *
21349
 * <example name="ngList-directive" module="listExample">
21350
 *   <file name="app.js">
21351
 *      angular.module('listExample', [])
21352
 *        .controller('ExampleController', ['$scope', function($scope) {
21353
 *          $scope.names = ['morpheus', 'neo', 'trinity'];
21354
 *        }]);
21355
 *   </file>
21356
 *   <file name="index.html">
21357
 *    <form name="myForm" ng-controller="ExampleController">
21358
 *      List: <input name="namesInput" ng-model="names" ng-list required>
21359
 *      <span class="error" ng-show="myForm.namesInput.$error.required">
21360
 *        Required!</span>
21361
 *      <br>
21362
 *      <tt>names = {{names}}</tt><br/>
21363
 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
21364
 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
21365
 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21366
 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21367
 *     </form>
21368
 *   </file>
21369
 *   <file name="protractor.js" type="protractor">
21370
 *     var listInput = element(by.model('names'));
21371
 *     var names = element(by.exactBinding('names'));
21372
 *     var valid = element(by.binding('myForm.namesInput.$valid'));
21373
 *     var error = element(by.css('span.error'));
21374
 *
21375
 *     it('should initialize to model', function() {
21376
 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
21377
 *       expect(valid.getText()).toContain('true');
21378
 *       expect(error.getCssValue('display')).toBe('none');
21379
 *     });
21380
 *
21381
 *     it('should be invalid if empty', function() {
21382
 *       listInput.clear();
21383
 *       listInput.sendKeys('');
21384
 *
21385
 *       expect(names.getText()).toContain('');
21386
 *       expect(valid.getText()).toContain('false');
21387
 *       expect(error.getCssValue('display')).not.toBe('none');
21388
 *     });
21389
 *   </file>
21390
 * </example>
21391
 *
21392
 * ### Example - splitting on whitespace
21393
 * <example name="ngList-directive-newlines">
21394
 *   <file name="index.html">
21395
 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
21396
 *    <pre>{{ list | json }}</pre>
21397
 *   </file>
21398
 *   <file name="protractor.js" type="protractor">
21399
 *     it("should split the text by newlines", function() {
21400
 *       var listInput = element(by.model('list'));
21401
 *       var output = element(by.binding('list | json'));
21402
 *       listInput.sendKeys('abc\ndef\nghi');
21403
 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
21404
 *     });
21405
 *   </file>
21406
 * </example>
21407
 *
21408
 * @element input
21409
 * @param {string=} ngList optional delimiter that should be used to split the value.
21410
 */
21411
var ngListDirective = function() {
21412
  return {
21413
    restrict: 'A',
21414
    priority: 100,
21415
    require: 'ngModel',
21416
    link: function(scope, element, attr, ctrl) {
21417
      // We want to control whitespace trimming so we use this convoluted approach
21418
      // to access the ngList attribute, which doesn't pre-trim the attribute
21419
      var ngList = element.attr(attr.$attr.ngList) || ', ';
21420
      var trimValues = attr.ngTrim !== 'false';
21421
      var separator = trimValues ? trim(ngList) : ngList;
21422
 
21423
      var parse = function(viewValue) {
21424
        // If the viewValue is invalid (say required but empty) it will be `undefined`
21425
        if (isUndefined(viewValue)) return;
21426
 
21427
        var list = [];
21428
 
21429
        if (viewValue) {
21430
          forEach(viewValue.split(separator), function(value) {
21431
            if (value) list.push(trimValues ? trim(value) : value);
21432
          });
21433
        }
21434
 
21435
        return list;
21436
      };
21437
 
21438
      ctrl.$parsers.push(parse);
21439
      ctrl.$formatters.push(function(value) {
21440
        if (isArray(value)) {
21441
          return value.join(ngList);
21442
        }
21443
 
21444
        return undefined;
21445
      });
21446
 
21447
      // Override the standard $isEmpty because an empty array means the input is empty.
21448
      ctrl.$isEmpty = function(value) {
21449
        return !value || !value.length;
21450
      };
21451
    }
21452
  };
21453
};
21454
 
21455
 
21456
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
21457
/**
21458
 * @ngdoc directive
21459
 * @name ngValue
21460
 *
21461
 * @description
21462
 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
21463
 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
21464
 * the bound value.
21465
 *
21466
 * `ngValue` is useful when dynamically generating lists of radio buttons using
21467
 * {@link ngRepeat `ngRepeat`}, as shown below.
21468
 *
21469
 * Likewise, `ngValue` can be used to generate `<option>` elements for
21470
 * the {@link select `select`} element. In that case however, only strings are supported
21471
 * for the `value `attribute, so the resulting `ngModel` will always be a string.
21472
 * Support for `select` models with non-string values is available via `ngOptions`.
21473
 *
21474
 * @element input
21475
 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
21476
 *   of the `input` element
21477
 *
21478
 * @example
21479
    <example name="ngValue-directive" module="valueExample">
21480
      <file name="index.html">
21481
       <script>
21482
          angular.module('valueExample', [])
21483
            .controller('ExampleController', ['$scope', function($scope) {
21484
              $scope.names = ['pizza', 'unicorns', 'robots'];
21485
              $scope.my = { favorite: 'unicorns' };
21486
            }]);
21487
       </script>
21488
        <form ng-controller="ExampleController">
21489
          <h2>Which is your favorite?</h2>
21490
            <label ng-repeat="name in names" for="{{name}}">
21491
              {{name}}
21492
              <input type="radio"
21493
                     ng-model="my.favorite"
21494
                     ng-value="name"
21495
                     id="{{name}}"
21496
                     name="favorite">
21497
            </label>
21498
          <div>You chose {{my.favorite}}</div>
21499
        </form>
21500
      </file>
21501
      <file name="protractor.js" type="protractor">
21502
        var favorite = element(by.binding('my.favorite'));
21503
 
21504
        it('should initialize to model', function() {
21505
          expect(favorite.getText()).toContain('unicorns');
21506
        });
21507
        it('should bind the values to the inputs', function() {
21508
          element.all(by.model('my.favorite')).get(0).click();
21509
          expect(favorite.getText()).toContain('pizza');
21510
        });
21511
      </file>
21512
    </example>
21513
 */
21514
var ngValueDirective = function() {
21515
  return {
21516
    restrict: 'A',
21517
    priority: 100,
21518
    compile: function(tpl, tplAttr) {
21519
      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
21520
        return function ngValueConstantLink(scope, elm, attr) {
21521
          attr.$set('value', scope.$eval(attr.ngValue));
21522
        };
21523
      } else {
21524
        return function ngValueLink(scope, elm, attr) {
21525
          scope.$watch(attr.ngValue, function valueWatchAction(value) {
21526
            attr.$set('value', value);
21527
          });
21528
        };
21529
      }
21530
    }
21531
  };
21532
};
21533
 
21534
/**
21535
 * @ngdoc directive
21536
 * @name ngModelOptions
21537
 *
21538
 * @description
21539
 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
21540
 * events that will trigger a model update and/or a debouncing delay so that the actual update only
21541
 * takes place when a timer expires; this timer will be reset after another change takes place.
21542
 *
21543
 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
21544
 * be different than the value in the actual model. This means that if you update the model you
21545
 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
21546
 * order to make sure it is synchronized with the model and that any debounced action is canceled.
21547
 *
21548
 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
21549
 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
21550
 * important because `form` controllers are published to the related scope under the name in their
21551
 * `name` attribute.
21552
 *
21553
 * Any pending changes will take place immediately when an enclosing form is submitted via the
21554
 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
21555
 * to have access to the updated model.
21556
 *
21557
 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
21558
 *
21559
 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
21560
 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
21561
 *     events using an space delimited list. There is a special event called `default` that
21562
 *     matches the default events belonging of the control.
21563
 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
21564
 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
21565
 *     custom value for each event. For example:
21566
 *     `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
21567
 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
21568
 *     not validate correctly instead of the default behavior of setting the model to undefined.
21569
 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
21570
       `ngModel` as getters/setters.
21571
 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
21572
 *     `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
21573
 *     otherwise the default timezone of the browser will be used.
21574
 *
21575
 * @example
21576
 
21577
  The following example shows how to override immediate updates. Changes on the inputs within the
21578
  form will update the model only when the control loses focus (blur event). If `escape` key is
21579
  pressed while the input field is focused, the value is reset to the value in the current model.
21580
 
21581
  <example name="ngModelOptions-directive-blur" module="optionsExample">
21582
    <file name="index.html">
21583
      <div ng-controller="ExampleController">
21584
        <form name="userForm">
21585
          Name:
21586
          <input type="text" name="userName"
21587
                 ng-model="user.name"
21588
                 ng-model-options="{ updateOn: 'blur' }"
21589
                 ng-keyup="cancel($event)" /><br />
21590
 
21591
          Other data:
21592
          <input type="text" ng-model="user.data" /><br />
21593
        </form>
21594
        <pre>user.name = <span ng-bind="user.name"></span></pre>
21595
      </div>
21596
    </file>
21597
    <file name="app.js">
21598
      angular.module('optionsExample', [])
21599
        .controller('ExampleController', ['$scope', function($scope) {
21600
          $scope.user = { name: 'say', data: '' };
21601
 
21602
          $scope.cancel = function(e) {
21603
            if (e.keyCode == 27) {
21604
              $scope.userForm.userName.$rollbackViewValue();
21605
            }
21606
          };
21607
        }]);
21608
    </file>
21609
    <file name="protractor.js" type="protractor">
21610
      var model = element(by.binding('user.name'));
21611
      var input = element(by.model('user.name'));
21612
      var other = element(by.model('user.data'));
21613
 
21614
      it('should allow custom events', function() {
21615
        input.sendKeys(' hello');
21616
        input.click();
21617
        expect(model.getText()).toEqual('say');
21618
        other.click();
21619
        expect(model.getText()).toEqual('say hello');
21620
      });
21621
 
21622
      it('should $rollbackViewValue when model changes', function() {
21623
        input.sendKeys(' hello');
21624
        expect(input.getAttribute('value')).toEqual('say hello');
21625
        input.sendKeys(protractor.Key.ESCAPE);
21626
        expect(input.getAttribute('value')).toEqual('say');
21627
        other.click();
21628
        expect(model.getText()).toEqual('say');
21629
      });
21630
    </file>
21631
  </example>
21632
 
21633
  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
21634
  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
21635
 
21636
  <example name="ngModelOptions-directive-debounce" module="optionsExample">
21637
    <file name="index.html">
21638
      <div ng-controller="ExampleController">
21639
        <form name="userForm">
21640
          Name:
21641
          <input type="text" name="userName"
21642
                 ng-model="user.name"
21643
                 ng-model-options="{ debounce: 1000 }" />
21644
          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
21645
        </form>
21646
        <pre>user.name = <span ng-bind="user.name"></span></pre>
21647
      </div>
21648
    </file>
21649
    <file name="app.js">
21650
      angular.module('optionsExample', [])
21651
        .controller('ExampleController', ['$scope', function($scope) {
21652
          $scope.user = { name: 'say' };
21653
        }]);
21654
    </file>
21655
  </example>
21656
 
21657
  This one shows how to bind to getter/setters:
21658
 
21659
  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
21660
    <file name="index.html">
21661
      <div ng-controller="ExampleController">
21662
        <form name="userForm">
21663
          Name:
21664
          <input type="text" name="userName"
21665
                 ng-model="user.name"
21666
                 ng-model-options="{ getterSetter: true }" />
21667
        </form>
21668
        <pre>user.name = <span ng-bind="user.name()"></span></pre>
21669
      </div>
21670
    </file>
21671
    <file name="app.js">
21672
      angular.module('getterSetterExample', [])
21673
        .controller('ExampleController', ['$scope', function($scope) {
21674
          var _name = 'Brian';
21675
          $scope.user = {
21676
            name: function(newName) {
21677
              return angular.isDefined(newName) ? (_name = newName) : _name;
21678
            }
21679
          };
21680
        }]);
21681
    </file>
21682
  </example>
21683
 */
21684
var ngModelOptionsDirective = function() {
21685
  return {
21686
    restrict: 'A',
21687
    controller: ['$scope', '$attrs', function($scope, $attrs) {
21688
      var that = this;
21689
      this.$options = $scope.$eval($attrs.ngModelOptions);
21690
      // Allow adding/overriding bound events
21691
      if (this.$options.updateOn !== undefined) {
21692
        this.$options.updateOnDefault = false;
21693
        // extract "default" pseudo-event from list of events that can trigger a model update
21694
        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
21695
          that.$options.updateOnDefault = true;
21696
          return ' ';
21697
        }));
21698
      } else {
21699
        this.$options.updateOnDefault = true;
21700
      }
21701
    }]
21702
  };
21703
};
21704
 
21705
// helper methods
21706
function addSetValidityMethod(context) {
21707
  var ctrl = context.ctrl,
21708
      $element = context.$element,
21709
      classCache = {},
21710
      set = context.set,
21711
      unset = context.unset,
21712
      parentForm = context.parentForm,
21713
      $animate = context.$animate;
21714
 
21715
  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
21716
 
21717
  ctrl.$setValidity = setValidity;
21718
 
21719
  function setValidity(validationErrorKey, state, options) {
21720
    if (state === undefined) {
21721
      createAndSet('$pending', validationErrorKey, options);
21722
    } else {
21723
      unsetAndCleanup('$pending', validationErrorKey, options);
21724
    }
21725
    if (!isBoolean(state)) {
21726
      unset(ctrl.$error, validationErrorKey, options);
21727
      unset(ctrl.$$success, validationErrorKey, options);
21728
    } else {
21729
      if (state) {
21730
        unset(ctrl.$error, validationErrorKey, options);
21731
        set(ctrl.$$success, validationErrorKey, options);
21732
      } else {
21733
        set(ctrl.$error, validationErrorKey, options);
21734
        unset(ctrl.$$success, validationErrorKey, options);
21735
      }
21736
    }
21737
    if (ctrl.$pending) {
21738
      cachedToggleClass(PENDING_CLASS, true);
21739
      ctrl.$valid = ctrl.$invalid = undefined;
21740
      toggleValidationCss('', null);
21741
    } else {
21742
      cachedToggleClass(PENDING_CLASS, false);
21743
      ctrl.$valid = isObjectEmpty(ctrl.$error);
21744
      ctrl.$invalid = !ctrl.$valid;
21745
      toggleValidationCss('', ctrl.$valid);
21746
    }
21747
 
21748
    // re-read the state as the set/unset methods could have
21749
    // combined state in ctrl.$error[validationError] (used for forms),
21750
    // where setting/unsetting only increments/decrements the value,
21751
    // and does not replace it.
21752
    var combinedState;
21753
    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
21754
      combinedState = undefined;
21755
    } else if (ctrl.$error[validationErrorKey]) {
21756
      combinedState = false;
21757
    } else if (ctrl.$$success[validationErrorKey]) {
21758
      combinedState = true;
21759
    } else {
21760
      combinedState = null;
21761
    }
21762
    toggleValidationCss(validationErrorKey, combinedState);
21763
    parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
21764
  }
21765
 
21766
  function createAndSet(name, value, options) {
21767
    if (!ctrl[name]) {
21768
      ctrl[name] = {};
21769
    }
21770
    set(ctrl[name], value, options);
21771
  }
21772
 
21773
  function unsetAndCleanup(name, value, options) {
21774
    if (ctrl[name]) {
21775
      unset(ctrl[name], value, options);
21776
    }
21777
    if (isObjectEmpty(ctrl[name])) {
21778
      ctrl[name] = undefined;
21779
    }
21780
  }
21781
 
21782
  function cachedToggleClass(className, switchValue) {
21783
    if (switchValue && !classCache[className]) {
21784
      $animate.addClass($element, className);
21785
      classCache[className] = true;
21786
    } else if (!switchValue && classCache[className]) {
21787
      $animate.removeClass($element, className);
21788
      classCache[className] = false;
21789
    }
21790
  }
21791
 
21792
  function toggleValidationCss(validationErrorKey, isValid) {
21793
    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
21794
 
21795
    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
21796
    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
21797
  }
21798
}
21799
 
21800
function isObjectEmpty(obj) {
21801
  if (obj) {
21802
    for (var prop in obj) {
21803
      return false;
21804
    }
21805
  }
21806
  return true;
21807
}
21808
 
21809
/**
21810
 * @ngdoc directive
21811
 * @name ngBind
21812
 * @restrict AC
21813
 *
21814
 * @description
21815
 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
21816
 * with the value of a given expression, and to update the text content when the value of that
21817
 * expression changes.
21818
 *
21819
 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
21820
 * `{{ expression }}` which is similar but less verbose.
21821
 *
21822
 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
21823
 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
21824
 * element attribute, it makes the bindings invisible to the user while the page is loading.
21825
 *
21826
 * An alternative solution to this problem would be using the
21827
 * {@link ng.directive:ngCloak ngCloak} directive.
21828
 *
21829
 *
21830
 * @element ANY
21831
 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
21832
 *
21833
 * @example
21834
 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
21835
   <example module="bindExample">
21836
     <file name="index.html">
21837
       <script>
21838
         angular.module('bindExample', [])
21839
           .controller('ExampleController', ['$scope', function($scope) {
21840
             $scope.name = 'Whirled';
21841
           }]);
21842
       </script>
21843
       <div ng-controller="ExampleController">
21844
         Enter name: <input type="text" ng-model="name"><br>
21845
         Hello <span ng-bind="name"></span>!
21846
       </div>
21847
     </file>
21848
     <file name="protractor.js" type="protractor">
21849
       it('should check ng-bind', function() {
21850
         var nameInput = element(by.model('name'));
21851
 
21852
         expect(element(by.binding('name')).getText()).toBe('Whirled');
21853
         nameInput.clear();
21854
         nameInput.sendKeys('world');
21855
         expect(element(by.binding('name')).getText()).toBe('world');
21856
       });
21857
     </file>
21858
   </example>
21859
 */
21860
var ngBindDirective = ['$compile', function($compile) {
21861
  return {
21862
    restrict: 'AC',
21863
    compile: function ngBindCompile(templateElement) {
21864
      $compile.$$addBindingClass(templateElement);
21865
      return function ngBindLink(scope, element, attr) {
21866
        $compile.$$addBindingInfo(element, attr.ngBind);
21867
        element = element[0];
21868
        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
21869
          element.textContent = value === undefined ? '' : value;
21870
        });
21871
      };
21872
    }
21873
  };
21874
}];
21875
 
21876
 
21877
/**
21878
 * @ngdoc directive
21879
 * @name ngBindTemplate
21880
 *
21881
 * @description
21882
 * The `ngBindTemplate` directive specifies that the element
21883
 * text content should be replaced with the interpolation of the template
21884
 * in the `ngBindTemplate` attribute.
21885
 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
21886
 * expressions. This directive is needed since some HTML elements
21887
 * (such as TITLE and OPTION) cannot contain SPAN elements.
21888
 *
21889
 * @element ANY
21890
 * @param {string} ngBindTemplate template of form
21891
 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
21892
 *
21893
 * @example
21894
 * Try it here: enter text in text box and watch the greeting change.
21895
   <example module="bindExample">
21896
     <file name="index.html">
21897
       <script>
21898
         angular.module('bindExample', [])
21899
           .controller('ExampleController', ['$scope', function($scope) {
21900
             $scope.salutation = 'Hello';
21901
             $scope.name = 'World';
21902
           }]);
21903
       </script>
21904
       <div ng-controller="ExampleController">
21905
        Salutation: <input type="text" ng-model="salutation"><br>
21906
        Name: <input type="text" ng-model="name"><br>
21907
        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
21908
       </div>
21909
     </file>
21910
     <file name="protractor.js" type="protractor">
21911
       it('should check ng-bind', function() {
21912
         var salutationElem = element(by.binding('salutation'));
21913
         var salutationInput = element(by.model('salutation'));
21914
         var nameInput = element(by.model('name'));
21915
 
21916
         expect(salutationElem.getText()).toBe('Hello World!');
21917
 
21918
         salutationInput.clear();
21919
         salutationInput.sendKeys('Greetings');
21920
         nameInput.clear();
21921
         nameInput.sendKeys('user');
21922
 
21923
         expect(salutationElem.getText()).toBe('Greetings user!');
21924
       });
21925
     </file>
21926
   </example>
21927
 */
21928
var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
21929
  return {
21930
    compile: function ngBindTemplateCompile(templateElement) {
21931
      $compile.$$addBindingClass(templateElement);
21932
      return function ngBindTemplateLink(scope, element, attr) {
21933
        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
21934
        $compile.$$addBindingInfo(element, interpolateFn.expressions);
21935
        element = element[0];
21936
        attr.$observe('ngBindTemplate', function(value) {
21937
          element.textContent = value === undefined ? '' : value;
21938
        });
21939
      };
21940
    }
21941
  };
21942
}];
21943
 
21944
 
21945
/**
21946
 * @ngdoc directive
21947
 * @name ngBindHtml
21948
 *
21949
 * @description
21950
 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
21951
 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
21952
 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
21953
 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
21954
 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
21955
 *
21956
 * You may also bypass sanitization for values you know are safe. To do so, bind to
21957
 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
21958
 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
21959
 *
21960
 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
21961
 * will have an exception (instead of an exploit.)
21962
 *
21963
 * @element ANY
21964
 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
21965
 *
21966
 * @example
21967
 
21968
   <example module="bindHtmlExample" deps="angular-sanitize.js">
21969
     <file name="index.html">
21970
       <div ng-controller="ExampleController">
21971
        <p ng-bind-html="myHTML"></p>
21972
       </div>
21973
     </file>
21974
 
21975
     <file name="script.js">
21976
       angular.module('bindHtmlExample', ['ngSanitize'])
21977
         .controller('ExampleController', ['$scope', function($scope) {
21978
           $scope.myHTML =
21979
              'I am an <code>HTML</code>string with ' +
21980
              '<a href="#">links!</a> and other <em>stuff</em>';
21981
         }]);
21982
     </file>
21983
 
21984
     <file name="protractor.js" type="protractor">
21985
       it('should check ng-bind-html', function() {
21986
         expect(element(by.binding('myHTML')).getText()).toBe(
21987
             'I am an HTMLstring with links! and other stuff');
21988
       });
21989
     </file>
21990
   </example>
21991
 */
21992
var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
21993
  return {
21994
    restrict: 'A',
21995
    compile: function ngBindHtmlCompile(tElement, tAttrs) {
21996
      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
21997
      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
21998
        return (value || '').toString();
21999
      });
22000
      $compile.$$addBindingClass(tElement);
22001
 
22002
      return function ngBindHtmlLink(scope, element, attr) {
22003
        $compile.$$addBindingInfo(element, attr.ngBindHtml);
22004
 
22005
        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22006
          // we re-evaluate the expr because we want a TrustedValueHolderType
22007
          // for $sce, not a string
22008
          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22009
        });
22010
      };
22011
    }
22012
  };
22013
}];
22014
 
22015
function classDirective(name, selector) {
22016
  name = 'ngClass' + name;
22017
  return ['$animate', function($animate) {
22018
    return {
22019
      restrict: 'AC',
22020
      link: function(scope, element, attr) {
22021
        var oldVal;
22022
 
22023
        scope.$watch(attr[name], ngClassWatchAction, true);
22024
 
22025
        attr.$observe('class', function(value) {
22026
          ngClassWatchAction(scope.$eval(attr[name]));
22027
        });
22028
 
22029
 
22030
        if (name !== 'ngClass') {
22031
          scope.$watch('$index', function($index, old$index) {
22032
            // jshint bitwise: false
22033
            var mod = $index & 1;
22034
            if (mod !== (old$index & 1)) {
22035
              var classes = arrayClasses(scope.$eval(attr[name]));
22036
              mod === selector ?
22037
                addClasses(classes) :
22038
                removeClasses(classes);
22039
            }
22040
          });
22041
        }
22042
 
22043
        function addClasses(classes) {
22044
          var newClasses = digestClassCounts(classes, 1);
22045
          attr.$addClass(newClasses);
22046
        }
22047
 
22048
        function removeClasses(classes) {
22049
          var newClasses = digestClassCounts(classes, -1);
22050
          attr.$removeClass(newClasses);
22051
        }
22052
 
22053
        function digestClassCounts(classes, count) {
22054
          var classCounts = element.data('$classCounts') || {};
22055
          var classesToUpdate = [];
22056
          forEach(classes, function(className) {
22057
            if (count > 0 || classCounts[className]) {
22058
              classCounts[className] = (classCounts[className] || 0) + count;
22059
              if (classCounts[className] === +(count > 0)) {
22060
                classesToUpdate.push(className);
22061
              }
22062
            }
22063
          });
22064
          element.data('$classCounts', classCounts);
22065
          return classesToUpdate.join(' ');
22066
        }
22067
 
22068
        function updateClasses(oldClasses, newClasses) {
22069
          var toAdd = arrayDifference(newClasses, oldClasses);
22070
          var toRemove = arrayDifference(oldClasses, newClasses);
22071
          toAdd = digestClassCounts(toAdd, 1);
22072
          toRemove = digestClassCounts(toRemove, -1);
22073
          if (toAdd && toAdd.length) {
22074
            $animate.addClass(element, toAdd);
22075
          }
22076
          if (toRemove && toRemove.length) {
22077
            $animate.removeClass(element, toRemove);
22078
          }
22079
        }
22080
 
22081
        function ngClassWatchAction(newVal) {
22082
          if (selector === true || scope.$index % 2 === selector) {
22083
            var newClasses = arrayClasses(newVal || []);
22084
            if (!oldVal) {
22085
              addClasses(newClasses);
22086
            } else if (!equals(newVal,oldVal)) {
22087
              var oldClasses = arrayClasses(oldVal);
22088
              updateClasses(oldClasses, newClasses);
22089
            }
22090
          }
22091
          oldVal = shallowCopy(newVal);
22092
        }
22093
      }
22094
    };
22095
 
22096
    function arrayDifference(tokens1, tokens2) {
22097
      var values = [];
22098
 
22099
      outer:
22100
      for (var i = 0; i < tokens1.length; i++) {
22101
        var token = tokens1[i];
22102
        for (var j = 0; j < tokens2.length; j++) {
22103
          if (token == tokens2[j]) continue outer;
22104
        }
22105
        values.push(token);
22106
      }
22107
      return values;
22108
    }
22109
 
22110
    function arrayClasses(classVal) {
22111
      if (isArray(classVal)) {
22112
        return classVal;
22113
      } else if (isString(classVal)) {
22114
        return classVal.split(' ');
22115
      } else if (isObject(classVal)) {
22116
        var classes = [];
22117
        forEach(classVal, function(v, k) {
22118
          if (v) {
22119
            classes = classes.concat(k.split(' '));
22120
          }
22121
        });
22122
        return classes;
22123
      }
22124
      return classVal;
22125
    }
22126
  }];
22127
}
22128
 
22129
/**
22130
 * @ngdoc directive
22131
 * @name ngClass
22132
 * @restrict AC
22133
 *
22134
 * @description
22135
 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22136
 * an expression that represents all classes to be added.
22137
 *
22138
 * The directive operates in three different ways, depending on which of three types the expression
22139
 * evaluates to:
22140
 *
22141
 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22142
 * names.
22143
 *
22144
 * 2. If the expression evaluates to an array, each element of the array should be a string that is
22145
 * one or more space-delimited class names.
22146
 *
22147
 * 3. If the expression evaluates to an object, then for each key-value pair of the
22148
 * object with a truthy value the corresponding key is used as a class name.
22149
 *
22150
 * The directive won't add duplicate classes if a particular class was already set.
22151
 *
22152
 * When the expression changes, the previously added classes are removed and only then the
22153
 * new classes are added.
22154
 *
22155
 * @animations
22156
 * add - happens just before the class is applied to the element
22157
 * remove - happens just before the class is removed from the element
22158
 *
22159
 * @element ANY
22160
 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22161
 *   of the evaluation can be a string representing space delimited class
22162
 *   names, an array, or a map of class names to boolean values. In the case of a map, the
22163
 *   names of the properties whose values are truthy will be added as css classes to the
22164
 *   element.
22165
 *
22166
 * @example Example that demonstrates basic bindings via ngClass directive.
22167
   <example>
22168
     <file name="index.html">
22169
       <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
22170
       <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
22171
       <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
22172
       <input type="checkbox" ng-model="error"> error (apply "red" class)
22173
       <hr>
22174
       <p ng-class="style">Using String Syntax</p>
22175
       <input type="text" ng-model="style" placeholder="Type: bold strike red">
22176
       <hr>
22177
       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22178
       <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
22179
       <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
22180
       <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
22181
     </file>
22182
     <file name="style.css">
22183
       .strike {
22184
         text-decoration: line-through;
22185
       }
22186
       .bold {
22187
           font-weight: bold;
22188
       }
22189
       .red {
22190
           color: red;
22191
       }
22192
     </file>
22193
     <file name="protractor.js" type="protractor">
22194
       var ps = element.all(by.css('p'));
22195
 
22196
       it('should let you toggle the class', function() {
22197
 
22198
         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22199
         expect(ps.first().getAttribute('class')).not.toMatch(/red/);
22200
 
22201
         element(by.model('important')).click();
22202
         expect(ps.first().getAttribute('class')).toMatch(/bold/);
22203
 
22204
         element(by.model('error')).click();
22205
         expect(ps.first().getAttribute('class')).toMatch(/red/);
22206
       });
22207
 
22208
       it('should let you toggle string example', function() {
22209
         expect(ps.get(1).getAttribute('class')).toBe('');
22210
         element(by.model('style')).clear();
22211
         element(by.model('style')).sendKeys('red');
22212
         expect(ps.get(1).getAttribute('class')).toBe('red');
22213
       });
22214
 
22215
       it('array example should have 3 classes', function() {
22216
         expect(ps.last().getAttribute('class')).toBe('');
22217
         element(by.model('style1')).sendKeys('bold');
22218
         element(by.model('style2')).sendKeys('strike');
22219
         element(by.model('style3')).sendKeys('red');
22220
         expect(ps.last().getAttribute('class')).toBe('bold strike red');
22221
       });
22222
     </file>
22223
   </example>
22224
 
22225
   ## Animations
22226
 
22227
   The example below demonstrates how to perform animations using ngClass.
22228
 
22229
   <example module="ngAnimate" deps="angular-animate.js" animations="true">
22230
     <file name="index.html">
22231
      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22232
      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22233
      <br>
22234
      <span class="base-class" ng-class="myVar">Sample Text</span>
22235
     </file>
22236
     <file name="style.css">
22237
       .base-class {
22238
         -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22239
         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22240
       }
22241
 
22242
       .base-class.my-class {
22243
         color: red;
22244
         font-size:3em;
22245
       }
22246
     </file>
22247
     <file name="protractor.js" type="protractor">
22248
       it('should check ng-class', function() {
22249
         expect(element(by.css('.base-class')).getAttribute('class')).not.
22250
           toMatch(/my-class/);
22251
 
22252
         element(by.id('setbtn')).click();
22253
 
22254
         expect(element(by.css('.base-class')).getAttribute('class')).
22255
           toMatch(/my-class/);
22256
 
22257
         element(by.id('clearbtn')).click();
22258
 
22259
         expect(element(by.css('.base-class')).getAttribute('class')).not.
22260
           toMatch(/my-class/);
22261
       });
22262
     </file>
22263
   </example>
22264
 
22265
 
22266
   ## ngClass and pre-existing CSS3 Transitions/Animations
22267
   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
22268
   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
22269
   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
22270
   to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and
22271
   {@link ng.$animate#removeClass $animate.removeClass}.
22272
 */
22273
var ngClassDirective = classDirective('', true);
22274
 
22275
/**
22276
 * @ngdoc directive
22277
 * @name ngClassOdd
22278
 * @restrict AC
22279
 *
22280
 * @description
22281
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22282
 * {@link ng.directive:ngClass ngClass}, except they work in
22283
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22284
 *
22285
 * This directive can be applied only within the scope of an
22286
 * {@link ng.directive:ngRepeat ngRepeat}.
22287
 *
22288
 * @element ANY
22289
 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
22290
 *   of the evaluation can be a string representing space delimited class names or an array.
22291
 *
22292
 * @example
22293
   <example>
22294
     <file name="index.html">
22295
        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22296
          <li ng-repeat="name in names">
22297
           <span ng-class-odd="'odd'" ng-class-even="'even'">
22298
             {{name}}
22299
           </span>
22300
          </li>
22301
        </ol>
22302
     </file>
22303
     <file name="style.css">
22304
       .odd {
22305
         color: red;
22306
       }
22307
       .even {
22308
         color: blue;
22309
       }
22310
     </file>
22311
     <file name="protractor.js" type="protractor">
22312
       it('should check ng-class-odd and ng-class-even', function() {
22313
         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22314
           toMatch(/odd/);
22315
         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22316
           toMatch(/even/);
22317
       });
22318
     </file>
22319
   </example>
22320
 */
22321
var ngClassOddDirective = classDirective('Odd', 0);
22322
 
22323
/**
22324
 * @ngdoc directive
22325
 * @name ngClassEven
22326
 * @restrict AC
22327
 *
22328
 * @description
22329
 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22330
 * {@link ng.directive:ngClass ngClass}, except they work in
22331
 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22332
 *
22333
 * This directive can be applied only within the scope of an
22334
 * {@link ng.directive:ngRepeat ngRepeat}.
22335
 *
22336
 * @element ANY
22337
 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
22338
 *   result of the evaluation can be a string representing space delimited class names or an array.
22339
 *
22340
 * @example
22341
   <example>
22342
     <file name="index.html">
22343
        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22344
          <li ng-repeat="name in names">
22345
           <span ng-class-odd="'odd'" ng-class-even="'even'">
22346
             {{name}} &nbsp; &nbsp; &nbsp;
22347
           </span>
22348
          </li>
22349
        </ol>
22350
     </file>
22351
     <file name="style.css">
22352
       .odd {
22353
         color: red;
22354
       }
22355
       .even {
22356
         color: blue;
22357
       }
22358
     </file>
22359
     <file name="protractor.js" type="protractor">
22360
       it('should check ng-class-odd and ng-class-even', function() {
22361
         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22362
           toMatch(/odd/);
22363
         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22364
           toMatch(/even/);
22365
       });
22366
     </file>
22367
   </example>
22368
 */
22369
var ngClassEvenDirective = classDirective('Even', 1);
22370
 
22371
/**
22372
 * @ngdoc directive
22373
 * @name ngCloak
22374
 * @restrict AC
22375
 *
22376
 * @description
22377
 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
22378
 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
22379
 * directive to avoid the undesirable flicker effect caused by the html template display.
22380
 *
22381
 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
22382
 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
22383
 * of the browser view.
22384
 *
22385
 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
22386
 * `angular.min.js`.
22387
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
22388
 *
22389
 * ```css
22390
 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
22391
 *   display: none !important;
22392
 * }
22393
 * ```
22394
 *
22395
 * When this css rule is loaded by the browser, all html elements (including their children) that
22396
 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
22397
 * during the compilation of the template it deletes the `ngCloak` element attribute, making
22398
 * the compiled element visible.
22399
 *
22400
 * For the best result, the `angular.js` script must be loaded in the head section of the html
22401
 * document; alternatively, the css rule above must be included in the external stylesheet of the
22402
 * application.
22403
 *
22404
 * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
22405
 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
22406
 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
22407
 *
22408
 * @element ANY
22409
 *
22410
 * @example
22411
   <example>
22412
     <file name="index.html">
22413
        <div id="template1" ng-cloak>{{ 'hello' }}</div>
22414
        <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
22415
     </file>
22416
     <file name="protractor.js" type="protractor">
22417
       it('should remove the template directive and css class', function() {
22418
         expect($('#template1').getAttribute('ng-cloak')).
22419
           toBeNull();
22420
         expect($('#template2').getAttribute('ng-cloak')).
22421
           toBeNull();
22422
       });
22423
     </file>
22424
   </example>
22425
 *
22426
 */
22427
var ngCloakDirective = ngDirective({
22428
  compile: function(element, attr) {
22429
    attr.$set('ngCloak', undefined);
22430
    element.removeClass('ng-cloak');
22431
  }
22432
});
22433
 
22434
/**
22435
 * @ngdoc directive
22436
 * @name ngController
22437
 *
22438
 * @description
22439
 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
22440
 * supports the principles behind the Model-View-Controller design pattern.
22441
 *
22442
 * MVC components in angular:
22443
 *
22444
 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
22445
 *   are accessed through bindings.
22446
 * * View — The template (HTML with data bindings) that is rendered into the View.
22447
 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
22448
 *   logic behind the application to decorate the scope with functions and values
22449
 *
22450
 * Note that you can also attach controllers to the DOM by declaring it in a route definition
22451
 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
22452
 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
22453
 * and executed twice.
22454
 *
22455
 * @element ANY
22456
 * @scope
22457
 * @priority 500
22458
 * @param {expression} ngController Name of a constructor function registered with the current
22459
 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
22460
 * that on the current scope evaluates to a constructor function.
22461
 *
22462
 * The controller instance can be published into a scope property by specifying
22463
 * `ng-controller="as propertyName"`.
22464
 *
22465
 * If the current `$controllerProvider` is configured to use globals (via
22466
 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
22467
 * also be the name of a globally accessible constructor function (not recommended).
22468
 *
22469
 * @example
22470
 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
22471
 * greeting are methods declared on the controller (see source tab). These methods can
22472
 * easily be called from the angular markup. Any changes to the data are automatically reflected
22473
 * in the View without the need for a manual update.
22474
 *
22475
 * Two different declaration styles are included below:
22476
 *
22477
 * * one binds methods and properties directly onto the controller using `this`:
22478
 * `ng-controller="SettingsController1 as settings"`
22479
 * * one injects `$scope` into the controller:
22480
 * `ng-controller="SettingsController2"`
22481
 *
22482
 * The second option is more common in the Angular community, and is generally used in boilerplates
22483
 * and in this guide. However, there are advantages to binding properties directly to the controller
22484
 * and avoiding scope.
22485
 *
22486
 * * Using `controller as` makes it obvious which controller you are accessing in the template when
22487
 * multiple controllers apply to an element.
22488
 * * If you are writing your controllers as classes you have easier access to the properties and
22489
 * methods, which will appear on the scope, from inside the controller code.
22490
 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
22491
 * inheritance masking primitives.
22492
 *
22493
 * This example demonstrates the `controller as` syntax.
22494
 *
22495
 * <example name="ngControllerAs" module="controllerAsExample">
22496
 *   <file name="index.html">
22497
 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
22498
 *      Name: <input type="text" ng-model="settings.name"/>
22499
 *      [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
22500
 *      Contact:
22501
 *      <ul>
22502
 *        <li ng-repeat="contact in settings.contacts">
22503
 *          <select ng-model="contact.type">
22504
 *             <option>phone</option>
22505
 *             <option>email</option>
22506
 *          </select>
22507
 *          <input type="text" ng-model="contact.value"/>
22508
 *          [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
22509
 *          | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
22510
 *        </li>
22511
 *        <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
22512
 *     </ul>
22513
 *    </div>
22514
 *   </file>
22515
 *   <file name="app.js">
22516
 *    angular.module('controllerAsExample', [])
22517
 *      .controller('SettingsController1', SettingsController1);
22518
 *
22519
 *    function SettingsController1() {
22520
 *      this.name = "John Smith";
22521
 *      this.contacts = [
22522
 *        {type: 'phone', value: '408 555 1212'},
22523
 *        {type: 'email', value: 'john.smith@example.org'} ];
22524
 *    }
22525
 *
22526
 *    SettingsController1.prototype.greet = function() {
22527
 *      alert(this.name);
22528
 *    };
22529
 *
22530
 *    SettingsController1.prototype.addContact = function() {
22531
 *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
22532
 *    };
22533
 *
22534
 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
22535
 *     var index = this.contacts.indexOf(contactToRemove);
22536
 *      this.contacts.splice(index, 1);
22537
 *    };
22538
 *
22539
 *    SettingsController1.prototype.clearContact = function(contact) {
22540
 *      contact.type = 'phone';
22541
 *      contact.value = '';
22542
 *    };
22543
 *   </file>
22544
 *   <file name="protractor.js" type="protractor">
22545
 *     it('should check controller as', function() {
22546
 *       var container = element(by.id('ctrl-as-exmpl'));
22547
 *         expect(container.element(by.model('settings.name'))
22548
 *           .getAttribute('value')).toBe('John Smith');
22549
 *
22550
 *       var firstRepeat =
22551
 *           container.element(by.repeater('contact in settings.contacts').row(0));
22552
 *       var secondRepeat =
22553
 *           container.element(by.repeater('contact in settings.contacts').row(1));
22554
 *
22555
 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22556
 *           .toBe('408 555 1212');
22557
 *
22558
 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22559
 *           .toBe('john.smith@example.org');
22560
 *
22561
 *       firstRepeat.element(by.linkText('clear')).click();
22562
 *
22563
 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22564
 *           .toBe('');
22565
 *
22566
 *       container.element(by.linkText('add')).click();
22567
 *
22568
 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
22569
 *           .element(by.model('contact.value'))
22570
 *           .getAttribute('value'))
22571
 *           .toBe('yourname@example.org');
22572
 *     });
22573
 *   </file>
22574
 * </example>
22575
 *
22576
 * This example demonstrates the "attach to `$scope`" style of controller.
22577
 *
22578
 * <example name="ngController" module="controllerExample">
22579
 *  <file name="index.html">
22580
 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
22581
 *     Name: <input type="text" ng-model="name"/>
22582
 *     [ <a href="" ng-click="greet()">greet</a> ]<br/>
22583
 *     Contact:
22584
 *     <ul>
22585
 *       <li ng-repeat="contact in contacts">
22586
 *         <select ng-model="contact.type">
22587
 *            <option>phone</option>
22588
 *            <option>email</option>
22589
 *         </select>
22590
 *         <input type="text" ng-model="contact.value"/>
22591
 *         [ <a href="" ng-click="clearContact(contact)">clear</a>
22592
 *         | <a href="" ng-click="removeContact(contact)">X</a> ]
22593
 *       </li>
22594
 *       <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
22595
 *    </ul>
22596
 *   </div>
22597
 *  </file>
22598
 *  <file name="app.js">
22599
 *   angular.module('controllerExample', [])
22600
 *     .controller('SettingsController2', ['$scope', SettingsController2]);
22601
 *
22602
 *   function SettingsController2($scope) {
22603
 *     $scope.name = "John Smith";
22604
 *     $scope.contacts = [
22605
 *       {type:'phone', value:'408 555 1212'},
22606
 *       {type:'email', value:'john.smith@example.org'} ];
22607
 *
22608
 *     $scope.greet = function() {
22609
 *       alert($scope.name);
22610
 *     };
22611
 *
22612
 *     $scope.addContact = function() {
22613
 *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
22614
 *     };
22615
 *
22616
 *     $scope.removeContact = function(contactToRemove) {
22617
 *       var index = $scope.contacts.indexOf(contactToRemove);
22618
 *       $scope.contacts.splice(index, 1);
22619
 *     };
22620
 *
22621
 *     $scope.clearContact = function(contact) {
22622
 *       contact.type = 'phone';
22623
 *       contact.value = '';
22624
 *     };
22625
 *   }
22626
 *  </file>
22627
 *  <file name="protractor.js" type="protractor">
22628
 *    it('should check controller', function() {
22629
 *      var container = element(by.id('ctrl-exmpl'));
22630
 *
22631
 *      expect(container.element(by.model('name'))
22632
 *          .getAttribute('value')).toBe('John Smith');
22633
 *
22634
 *      var firstRepeat =
22635
 *          container.element(by.repeater('contact in contacts').row(0));
22636
 *      var secondRepeat =
22637
 *          container.element(by.repeater('contact in contacts').row(1));
22638
 *
22639
 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22640
 *          .toBe('408 555 1212');
22641
 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22642
 *          .toBe('john.smith@example.org');
22643
 *
22644
 *      firstRepeat.element(by.linkText('clear')).click();
22645
 *
22646
 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22647
 *          .toBe('');
22648
 *
22649
 *      container.element(by.linkText('add')).click();
22650
 *
22651
 *      expect(container.element(by.repeater('contact in contacts').row(2))
22652
 *          .element(by.model('contact.value'))
22653
 *          .getAttribute('value'))
22654
 *          .toBe('yourname@example.org');
22655
 *    });
22656
 *  </file>
22657
 *</example>
22658
 
22659
 */
22660
var ngControllerDirective = [function() {
22661
  return {
22662
    restrict: 'A',
22663
    scope: true,
22664
    controller: '@',
22665
    priority: 500
22666
  };
22667
}];
22668
 
22669
/**
22670
 * @ngdoc directive
22671
 * @name ngCsp
22672
 *
22673
 * @element html
22674
 * @description
22675
 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
22676
 *
22677
 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
22678
 *
22679
 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
22680
 * For Angular to be CSP compatible there are only two things that we need to do differently:
22681
 *
22682
 * - don't use `Function` constructor to generate optimized value getters
22683
 * - don't inject custom stylesheet into the document
22684
 *
22685
 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
22686
 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
22687
 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
22688
 * be raised.
22689
 *
22690
 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
22691
 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
22692
 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
22693
 *
22694
 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
22695
 * autodetection however triggers a CSP error to be logged in the console:
22696
 *
22697
 * ```
22698
 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
22699
 * script in the following Content Security Policy directive: "default-src 'self'". Note that
22700
 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
22701
 * ```
22702
 *
22703
 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
22704
 * directive on the root element of the application or on the `angular.js` script tag, whichever
22705
 * appears first in the html document.
22706
 *
22707
 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
22708
 *
22709
 * @example
22710
 * This example shows how to apply the `ngCsp` directive to the `html` tag.
22711
   ```html
22712
     <!doctype html>
22713
     <html ng-app ng-csp>
22714
     ...
22715
     ...
22716
     </html>
22717
   ```
22718
  * @example
22719
      // Note: the suffix `.csp` in the example name triggers
22720
      // csp mode in our http server!
22721
      <example name="example.csp" module="cspExample" ng-csp="true">
22722
        <file name="index.html">
22723
          <div ng-controller="MainController as ctrl">
22724
            <div>
22725
              <button ng-click="ctrl.inc()" id="inc">Increment</button>
22726
              <span id="counter">
22727
                {{ctrl.counter}}
22728
              </span>
22729
            </div>
22730
 
22731
            <div>
22732
              <button ng-click="ctrl.evil()" id="evil">Evil</button>
22733
              <span id="evilError">
22734
                {{ctrl.evilError}}
22735
              </span>
22736
            </div>
22737
          </div>
22738
        </file>
22739
        <file name="script.js">
22740
           angular.module('cspExample', [])
22741
             .controller('MainController', function() {
22742
                this.counter = 0;
22743
                this.inc = function() {
22744
                  this.counter++;
22745
                };
22746
                this.evil = function() {
22747
                  // jshint evil:true
22748
                  try {
22749
                    eval('1+2');
22750
                  } catch (e) {
22751
                    this.evilError = e.message;
22752
                  }
22753
                };
22754
              });
22755
        </file>
22756
        <file name="protractor.js" type="protractor">
22757
          var util, webdriver;
22758
 
22759
          var incBtn = element(by.id('inc'));
22760
          var counter = element(by.id('counter'));
22761
          var evilBtn = element(by.id('evil'));
22762
          var evilError = element(by.id('evilError'));
22763
 
22764
          function getAndClearSevereErrors() {
22765
            return browser.manage().logs().get('browser').then(function(browserLog) {
22766
              return browserLog.filter(function(logEntry) {
22767
                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
22768
              });
22769
            });
22770
          }
22771
 
22772
          function clearErrors() {
22773
            getAndClearSevereErrors();
22774
          }
22775
 
22776
          function expectNoErrors() {
22777
            getAndClearSevereErrors().then(function(filteredLog) {
22778
              expect(filteredLog.length).toEqual(0);
22779
              if (filteredLog.length) {
22780
                console.log('browser console errors: ' + util.inspect(filteredLog));
22781
              }
22782
            });
22783
          }
22784
 
22785
          function expectError(regex) {
22786
            getAndClearSevereErrors().then(function(filteredLog) {
22787
              var found = false;
22788
              filteredLog.forEach(function(log) {
22789
                if (log.message.match(regex)) {
22790
                  found = true;
22791
                }
22792
              });
22793
              if (!found) {
22794
                throw new Error('expected an error that matches ' + regex);
22795
              }
22796
            });
22797
          }
22798
 
22799
          beforeEach(function() {
22800
            util = require('util');
22801
            webdriver = require('protractor/node_modules/selenium-webdriver');
22802
          });
22803
 
22804
          // For now, we only test on Chrome,
22805
          // as Safari does not load the page with Protractor's injected scripts,
22806
          // and Firefox webdriver always disables content security policy (#6358)
22807
          if (browser.params.browser !== 'chrome') {
22808
            return;
22809
          }
22810
 
22811
          it('should not report errors when the page is loaded', function() {
22812
            // clear errors so we are not dependent on previous tests
22813
            clearErrors();
22814
            // Need to reload the page as the page is already loaded when
22815
            // we come here
22816
            browser.driver.getCurrentUrl().then(function(url) {
22817
              browser.get(url);
22818
            });
22819
            expectNoErrors();
22820
          });
22821
 
22822
          it('should evaluate expressions', function() {
22823
            expect(counter.getText()).toEqual('0');
22824
            incBtn.click();
22825
            expect(counter.getText()).toEqual('1');
22826
            expectNoErrors();
22827
          });
22828
 
22829
          it('should throw and report an error when using "eval"', function() {
22830
            evilBtn.click();
22831
            expect(evilError.getText()).toMatch(/Content Security Policy/);
22832
            expectError(/Content Security Policy/);
22833
          });
22834
        </file>
22835
      </example>
22836
  */
22837
 
22838
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
22839
// bootstrap the system (before $parse is instantiated), for this reason we just have
22840
// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
22841
 
22842
/**
22843
 * @ngdoc directive
22844
 * @name ngClick
22845
 *
22846
 * @description
22847
 * The ngClick directive allows you to specify custom behavior when
22848
 * an element is clicked.
22849
 *
22850
 * @element ANY
22851
 * @priority 0
22852
 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
22853
 * click. ({@link guide/expression#-event- Event object is available as `$event`})
22854
 *
22855
 * @example
22856
   <example>
22857
     <file name="index.html">
22858
      <button ng-click="count = count + 1" ng-init="count=0">
22859
        Increment
22860
      </button>
22861
      <span>
22862
        count: {{count}}
22863
      </span>
22864
     </file>
22865
     <file name="protractor.js" type="protractor">
22866
       it('should check ng-click', function() {
22867
         expect(element(by.binding('count')).getText()).toMatch('0');
22868
         element(by.css('button')).click();
22869
         expect(element(by.binding('count')).getText()).toMatch('1');
22870
       });
22871
     </file>
22872
   </example>
22873
 */
22874
/*
22875
 * A collection of directives that allows creation of custom event handlers that are defined as
22876
 * angular expressions and are compiled and executed within the current scope.
22877
 */
22878
var ngEventDirectives = {};
22879
 
22880
// For events that might fire synchronously during DOM manipulation
22881
// we need to execute their event handlers asynchronously using $evalAsync,
22882
// so that they are not executed in an inconsistent state.
22883
var forceAsyncEvents = {
22884
  'blur': true,
22885
  'focus': true
22886
};
22887
forEach(
22888
  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
22889
  function(eventName) {
22890
    var directiveName = directiveNormalize('ng-' + eventName);
22891
    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
22892
      return {
22893
        restrict: 'A',
22894
        compile: function($element, attr) {
22895
          // We expose the powerful $event object on the scope that provides access to the Window,
22896
          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
22897
          // checks at the cost of speed since event handler expressions are not executed as
22898
          // frequently as regular change detection.
22899
          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
22900
          return function ngEventHandler(scope, element) {
22901
            element.on(eventName, function(event) {
22902
              var callback = function() {
22903
                fn(scope, {$event:event});
22904
              };
22905
              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
22906
                scope.$evalAsync(callback);
22907
              } else {
22908
                scope.$apply(callback);
22909
              }
22910
            });
22911
          };
22912
        }
22913
      };
22914
    }];
22915
  }
22916
);
22917
 
22918
/**
22919
 * @ngdoc directive
22920
 * @name ngDblclick
22921
 *
22922
 * @description
22923
 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
22924
 *
22925
 * @element ANY
22926
 * @priority 0
22927
 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
22928
 * a dblclick. (The Event object is available as `$event`)
22929
 *
22930
 * @example
22931
   <example>
22932
     <file name="index.html">
22933
      <button ng-dblclick="count = count + 1" ng-init="count=0">
22934
        Increment (on double click)
22935
      </button>
22936
      count: {{count}}
22937
     </file>
22938
   </example>
22939
 */
22940
 
22941
 
22942
/**
22943
 * @ngdoc directive
22944
 * @name ngMousedown
22945
 *
22946
 * @description
22947
 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
22948
 *
22949
 * @element ANY
22950
 * @priority 0
22951
 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
22952
 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
22953
 *
22954
 * @example
22955
   <example>
22956
     <file name="index.html">
22957
      <button ng-mousedown="count = count + 1" ng-init="count=0">
22958
        Increment (on mouse down)
22959
      </button>
22960
      count: {{count}}
22961
     </file>
22962
   </example>
22963
 */
22964
 
22965
 
22966
/**
22967
 * @ngdoc directive
22968
 * @name ngMouseup
22969
 *
22970
 * @description
22971
 * Specify custom behavior on mouseup event.
22972
 *
22973
 * @element ANY
22974
 * @priority 0
22975
 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
22976
 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
22977
 *
22978
 * @example
22979
   <example>
22980
     <file name="index.html">
22981
      <button ng-mouseup="count = count + 1" ng-init="count=0">
22982
        Increment (on mouse up)
22983
      </button>
22984
      count: {{count}}
22985
     </file>
22986
   </example>
22987
 */
22988
 
22989
/**
22990
 * @ngdoc directive
22991
 * @name ngMouseover
22992
 *
22993
 * @description
22994
 * Specify custom behavior on mouseover event.
22995
 *
22996
 * @element ANY
22997
 * @priority 0
22998
 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
22999
 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23000
 *
23001
 * @example
23002
   <example>
23003
     <file name="index.html">
23004
      <button ng-mouseover="count = count + 1" ng-init="count=0">
23005
        Increment (when mouse is over)
23006
      </button>
23007
      count: {{count}}
23008
     </file>
23009
   </example>
23010
 */
23011
 
23012
 
23013
/**
23014
 * @ngdoc directive
23015
 * @name ngMouseenter
23016
 *
23017
 * @description
23018
 * Specify custom behavior on mouseenter event.
23019
 *
23020
 * @element ANY
23021
 * @priority 0
23022
 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23023
 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23024
 *
23025
 * @example
23026
   <example>
23027
     <file name="index.html">
23028
      <button ng-mouseenter="count = count + 1" ng-init="count=0">
23029
        Increment (when mouse enters)
23030
      </button>
23031
      count: {{count}}
23032
     </file>
23033
   </example>
23034
 */
23035
 
23036
 
23037
/**
23038
 * @ngdoc directive
23039
 * @name ngMouseleave
23040
 *
23041
 * @description
23042
 * Specify custom behavior on mouseleave event.
23043
 *
23044
 * @element ANY
23045
 * @priority 0
23046
 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23047
 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23048
 *
23049
 * @example
23050
   <example>
23051
     <file name="index.html">
23052
      <button ng-mouseleave="count = count + 1" ng-init="count=0">
23053
        Increment (when mouse leaves)
23054
      </button>
23055
      count: {{count}}
23056
     </file>
23057
   </example>
23058
 */
23059
 
23060
 
23061
/**
23062
 * @ngdoc directive
23063
 * @name ngMousemove
23064
 *
23065
 * @description
23066
 * Specify custom behavior on mousemove event.
23067
 *
23068
 * @element ANY
23069
 * @priority 0
23070
 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23071
 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23072
 *
23073
 * @example
23074
   <example>
23075
     <file name="index.html">
23076
      <button ng-mousemove="count = count + 1" ng-init="count=0">
23077
        Increment (when mouse moves)
23078
      </button>
23079
      count: {{count}}
23080
     </file>
23081
   </example>
23082
 */
23083
 
23084
 
23085
/**
23086
 * @ngdoc directive
23087
 * @name ngKeydown
23088
 *
23089
 * @description
23090
 * Specify custom behavior on keydown event.
23091
 *
23092
 * @element ANY
23093
 * @priority 0
23094
 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23095
 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23096
 *
23097
 * @example
23098
   <example>
23099
     <file name="index.html">
23100
      <input ng-keydown="count = count + 1" ng-init="count=0">
23101
      key down count: {{count}}
23102
     </file>
23103
   </example>
23104
 */
23105
 
23106
 
23107
/**
23108
 * @ngdoc directive
23109
 * @name ngKeyup
23110
 *
23111
 * @description
23112
 * Specify custom behavior on keyup event.
23113
 *
23114
 * @element ANY
23115
 * @priority 0
23116
 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23117
 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23118
 *
23119
 * @example
23120
   <example>
23121
     <file name="index.html">
23122
       <p>Typing in the input box below updates the key count</p>
23123
       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23124
 
23125
       <p>Typing in the input box below updates the keycode</p>
23126
       <input ng-keyup="event=$event">
23127
       <p>event keyCode: {{ event.keyCode }}</p>
23128
       <p>event altKey: {{ event.altKey }}</p>
23129
     </file>
23130
   </example>
23131
 */
23132
 
23133
 
23134
/**
23135
 * @ngdoc directive
23136
 * @name ngKeypress
23137
 *
23138
 * @description
23139
 * Specify custom behavior on keypress event.
23140
 *
23141
 * @element ANY
23142
 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23143
 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23144
 * and can be interrogated for keyCode, altKey, etc.)
23145
 *
23146
 * @example
23147
   <example>
23148
     <file name="index.html">
23149
      <input ng-keypress="count = count + 1" ng-init="count=0">
23150
      key press count: {{count}}
23151
     </file>
23152
   </example>
23153
 */
23154
 
23155
 
23156
/**
23157
 * @ngdoc directive
23158
 * @name ngSubmit
23159
 *
23160
 * @description
23161
 * Enables binding angular expressions to onsubmit events.
23162
 *
23163
 * Additionally it prevents the default action (which for form means sending the request to the
23164
 * server and reloading the current page), but only if the form does not contain `action`,
23165
 * `data-action`, or `x-action` attributes.
23166
 *
23167
 * <div class="alert alert-warning">
23168
 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23169
 * `ngSubmit` handlers together. See the
23170
 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23171
 * for a detailed discussion of when `ngSubmit` may be triggered.
23172
 * </div>
23173
 *
23174
 * @element form
23175
 * @priority 0
23176
 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23177
 * ({@link guide/expression#-event- Event object is available as `$event`})
23178
 *
23179
 * @example
23180
   <example module="submitExample">
23181
     <file name="index.html">
23182
      <script>
23183
        angular.module('submitExample', [])
23184
          .controller('ExampleController', ['$scope', function($scope) {
23185
            $scope.list = [];
23186
            $scope.text = 'hello';
23187
            $scope.submit = function() {
23188
              if ($scope.text) {
23189
                $scope.list.push(this.text);
23190
                $scope.text = '';
23191
              }
23192
            };
23193
          }]);
23194
      </script>
23195
      <form ng-submit="submit()" ng-controller="ExampleController">
23196
        Enter text and hit enter:
23197
        <input type="text" ng-model="text" name="text" />
23198
        <input type="submit" id="submit" value="Submit" />
23199
        <pre>list={{list}}</pre>
23200
      </form>
23201
     </file>
23202
     <file name="protractor.js" type="protractor">
23203
       it('should check ng-submit', function() {
23204
         expect(element(by.binding('list')).getText()).toBe('list=[]');
23205
         element(by.css('#submit')).click();
23206
         expect(element(by.binding('list')).getText()).toContain('hello');
23207
         expect(element(by.model('text')).getAttribute('value')).toBe('');
23208
       });
23209
       it('should ignore empty strings', function() {
23210
         expect(element(by.binding('list')).getText()).toBe('list=[]');
23211
         element(by.css('#submit')).click();
23212
         element(by.css('#submit')).click();
23213
         expect(element(by.binding('list')).getText()).toContain('hello');
23214
        });
23215
     </file>
23216
   </example>
23217
 */
23218
 
23219
/**
23220
 * @ngdoc directive
23221
 * @name ngFocus
23222
 *
23223
 * @description
23224
 * Specify custom behavior on focus event.
23225
 *
23226
 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
23227
 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23228
 * during an `$apply` to ensure a consistent state.
23229
 *
23230
 * @element window, input, select, textarea, a
23231
 * @priority 0
23232
 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
23233
 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
23234
 *
23235
 * @example
23236
 * See {@link ng.directive:ngClick ngClick}
23237
 */
23238
 
23239
/**
23240
 * @ngdoc directive
23241
 * @name ngBlur
23242
 *
23243
 * @description
23244
 * Specify custom behavior on blur event.
23245
 *
23246
 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
23247
 * an element has lost focus.
23248
 *
23249
 * Note: As the `blur` event is executed synchronously also during DOM manipulations
23250
 * (e.g. removing a focussed input),
23251
 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23252
 * during an `$apply` to ensure a consistent state.
23253
 *
23254
 * @element window, input, select, textarea, a
23255
 * @priority 0
23256
 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
23257
 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
23258
 *
23259
 * @example
23260
 * See {@link ng.directive:ngClick ngClick}
23261
 */
23262
 
23263
/**
23264
 * @ngdoc directive
23265
 * @name ngCopy
23266
 *
23267
 * @description
23268
 * Specify custom behavior on copy event.
23269
 *
23270
 * @element window, input, select, textarea, a
23271
 * @priority 0
23272
 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
23273
 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
23274
 *
23275
 * @example
23276
   <example>
23277
     <file name="index.html">
23278
      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
23279
      copied: {{copied}}
23280
     </file>
23281
   </example>
23282
 */
23283
 
23284
/**
23285
 * @ngdoc directive
23286
 * @name ngCut
23287
 *
23288
 * @description
23289
 * Specify custom behavior on cut event.
23290
 *
23291
 * @element window, input, select, textarea, a
23292
 * @priority 0
23293
 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
23294
 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
23295
 *
23296
 * @example
23297
   <example>
23298
     <file name="index.html">
23299
      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
23300
      cut: {{cut}}
23301
     </file>
23302
   </example>
23303
 */
23304
 
23305
/**
23306
 * @ngdoc directive
23307
 * @name ngPaste
23308
 *
23309
 * @description
23310
 * Specify custom behavior on paste event.
23311
 *
23312
 * @element window, input, select, textarea, a
23313
 * @priority 0
23314
 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
23315
 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
23316
 *
23317
 * @example
23318
   <example>
23319
     <file name="index.html">
23320
      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
23321
      pasted: {{paste}}
23322
     </file>
23323
   </example>
23324
 */
23325
 
23326
/**
23327
 * @ngdoc directive
23328
 * @name ngIf
23329
 * @restrict A
23330
 *
23331
 * @description
23332
 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
23333
 * {expression}. If the expression assigned to `ngIf` evaluates to a false
23334
 * value then the element is removed from the DOM, otherwise a clone of the
23335
 * element is reinserted into the DOM.
23336
 *
23337
 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
23338
 * element in the DOM rather than changing its visibility via the `display` css property.  A common
23339
 * case when this difference is significant is when using css selectors that rely on an element's
23340
 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
23341
 *
23342
 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
23343
 * is created when the element is restored.  The scope created within `ngIf` inherits from
23344
 * its parent scope using
23345
 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
23346
 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
23347
 * a javascript primitive defined in the parent scope. In this case any modifications made to the
23348
 * variable within the child scope will override (hide) the value in the parent scope.
23349
 *
23350
 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
23351
 * is if an element's class attribute is directly modified after it's compiled, using something like
23352
 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
23353
 * the added class will be lost because the original compiled state is used to regenerate the element.
23354
 *
23355
 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
23356
 * and `leave` effects.
23357
 *
23358
 * @animations
23359
 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
23360
 * leave - happens just before the `ngIf` contents are removed from the DOM
23361
 *
23362
 * @element ANY
23363
 * @scope
23364
 * @priority 600
23365
 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
23366
 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
23367
 *     element is added to the DOM tree.
23368
 *
23369
 * @example
23370
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
23371
    <file name="index.html">
23372
      Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
23373
      Show when checked:
23374
      <span ng-if="checked" class="animate-if">
23375
        This is removed when the checkbox is unchecked.
23376
      </span>
23377
    </file>
23378
    <file name="animations.css">
23379
      .animate-if {
23380
        background:white;
23381
        border:1px solid black;
23382
        padding:10px;
23383
      }
23384
 
23385
      .animate-if.ng-enter, .animate-if.ng-leave {
23386
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23387
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23388
      }
23389
 
23390
      .animate-if.ng-enter,
23391
      .animate-if.ng-leave.ng-leave-active {
23392
        opacity:0;
23393
      }
23394
 
23395
      .animate-if.ng-leave,
23396
      .animate-if.ng-enter.ng-enter-active {
23397
        opacity:1;
23398
      }
23399
    </file>
23400
  </example>
23401
 */
23402
var ngIfDirective = ['$animate', function($animate) {
23403
  return {
23404
    multiElement: true,
23405
    transclude: 'element',
23406
    priority: 600,
23407
    terminal: true,
23408
    restrict: 'A',
23409
    $$tlb: true,
23410
    link: function($scope, $element, $attr, ctrl, $transclude) {
23411
        var block, childScope, previousElements;
23412
        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
23413
 
23414
          if (value) {
23415
            if (!childScope) {
23416
              $transclude(function(clone, newScope) {
23417
                childScope = newScope;
23418
                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
23419
                // Note: We only need the first/last node of the cloned nodes.
23420
                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
23421
                // by a directive with templateUrl when its template arrives.
23422
                block = {
23423
                  clone: clone
23424
                };
23425
                $animate.enter(clone, $element.parent(), $element);
23426
              });
23427
            }
23428
          } else {
23429
            if (previousElements) {
23430
              previousElements.remove();
23431
              previousElements = null;
23432
            }
23433
            if (childScope) {
23434
              childScope.$destroy();
23435
              childScope = null;
23436
            }
23437
            if (block) {
23438
              previousElements = getBlockNodes(block.clone);
23439
              $animate.leave(previousElements).then(function() {
23440
                previousElements = null;
23441
              });
23442
              block = null;
23443
            }
23444
          }
23445
        });
23446
    }
23447
  };
23448
}];
23449
 
23450
/**
23451
 * @ngdoc directive
23452
 * @name ngInclude
23453
 * @restrict ECA
23454
 *
23455
 * @description
23456
 * Fetches, compiles and includes an external HTML fragment.
23457
 *
23458
 * By default, the template URL is restricted to the same domain and protocol as the
23459
 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
23460
 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
23461
 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
23462
 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
23463
 * ng.$sce Strict Contextual Escaping}.
23464
 *
23465
 * In addition, the browser's
23466
 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
23467
 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
23468
 * policy may further restrict whether the template is successfully loaded.
23469
 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
23470
 * access on some browsers.
23471
 *
23472
 * @animations
23473
 * enter - animation is used to bring new content into the browser.
23474
 * leave - animation is used to animate existing content away.
23475
 *
23476
 * The enter and leave animation occur concurrently.
23477
 *
23478
 * @scope
23479
 * @priority 400
23480
 *
23481
 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
23482
 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
23483
 * @param {string=} onload Expression to evaluate when a new partial is loaded.
23484
 *
23485
 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
23486
 *                  $anchorScroll} to scroll the viewport after the content is loaded.
23487
 *
23488
 *                  - If the attribute is not set, disable scrolling.
23489
 *                  - If the attribute is set without value, enable scrolling.
23490
 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
23491
 *
23492
 * @example
23493
  <example module="includeExample" deps="angular-animate.js" animations="true">
23494
    <file name="index.html">
23495
     <div ng-controller="ExampleController">
23496
       <select ng-model="template" ng-options="t.name for t in templates">
23497
        <option value="">(blank)</option>
23498
       </select>
23499
       url of the template: <tt>{{template.url}}</tt>
23500
       <hr/>
23501
       <div class="slide-animate-container">
23502
         <div class="slide-animate" ng-include="template.url"></div>
23503
       </div>
23504
     </div>
23505
    </file>
23506
    <file name="script.js">
23507
      angular.module('includeExample', ['ngAnimate'])
23508
        .controller('ExampleController', ['$scope', function($scope) {
23509
          $scope.templates =
23510
            [ { name: 'template1.html', url: 'template1.html'},
23511
              { name: 'template2.html', url: 'template2.html'} ];
23512
          $scope.template = $scope.templates[0];
23513
        }]);
23514
     </file>
23515
    <file name="template1.html">
23516
      Content of template1.html
23517
    </file>
23518
    <file name="template2.html">
23519
      Content of template2.html
23520
    </file>
23521
    <file name="animations.css">
23522
      .slide-animate-container {
23523
        position:relative;
23524
        background:white;
23525
        border:1px solid black;
23526
        height:40px;
23527
        overflow:hidden;
23528
      }
23529
 
23530
      .slide-animate {
23531
        padding:10px;
23532
      }
23533
 
23534
      .slide-animate.ng-enter, .slide-animate.ng-leave {
23535
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23536
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23537
 
23538
        position:absolute;
23539
        top:0;
23540
        left:0;
23541
        right:0;
23542
        bottom:0;
23543
        display:block;
23544
        padding:10px;
23545
      }
23546
 
23547
      .slide-animate.ng-enter {
23548
        top:-50px;
23549
      }
23550
      .slide-animate.ng-enter.ng-enter-active {
23551
        top:0;
23552
      }
23553
 
23554
      .slide-animate.ng-leave {
23555
        top:0;
23556
      }
23557
      .slide-animate.ng-leave.ng-leave-active {
23558
        top:50px;
23559
      }
23560
    </file>
23561
    <file name="protractor.js" type="protractor">
23562
      var templateSelect = element(by.model('template'));
23563
      var includeElem = element(by.css('[ng-include]'));
23564
 
23565
      it('should load template1.html', function() {
23566
        expect(includeElem.getText()).toMatch(/Content of template1.html/);
23567
      });
23568
 
23569
      it('should load template2.html', function() {
23570
        if (browser.params.browser == 'firefox') {
23571
          // Firefox can't handle using selects
23572
          // See https://github.com/angular/protractor/issues/480
23573
          return;
23574
        }
23575
        templateSelect.click();
23576
        templateSelect.all(by.css('option')).get(2).click();
23577
        expect(includeElem.getText()).toMatch(/Content of template2.html/);
23578
      });
23579
 
23580
      it('should change to blank', function() {
23581
        if (browser.params.browser == 'firefox') {
23582
          // Firefox can't handle using selects
23583
          return;
23584
        }
23585
        templateSelect.click();
23586
        templateSelect.all(by.css('option')).get(0).click();
23587
        expect(includeElem.isPresent()).toBe(false);
23588
      });
23589
    </file>
23590
  </example>
23591
 */
23592
 
23593
 
23594
/**
23595
 * @ngdoc event
23596
 * @name ngInclude#$includeContentRequested
23597
 * @eventType emit on the scope ngInclude was declared in
23598
 * @description
23599
 * Emitted every time the ngInclude content is requested.
23600
 *
23601
 * @param {Object} angularEvent Synthetic event object.
23602
 * @param {String} src URL of content to load.
23603
 */
23604
 
23605
 
23606
/**
23607
 * @ngdoc event
23608
 * @name ngInclude#$includeContentLoaded
23609
 * @eventType emit on the current ngInclude scope
23610
 * @description
23611
 * Emitted every time the ngInclude content is reloaded.
23612
 *
23613
 * @param {Object} angularEvent Synthetic event object.
23614
 * @param {String} src URL of content to load.
23615
 */
23616
 
23617
 
23618
/**
23619
 * @ngdoc event
23620
 * @name ngInclude#$includeContentError
23621
 * @eventType emit on the scope ngInclude was declared in
23622
 * @description
23623
 * Emitted when a template HTTP request yields an erronous response (status < 200 || status > 299)
23624
 *
23625
 * @param {Object} angularEvent Synthetic event object.
23626
 * @param {String} src URL of content to load.
23627
 */
23628
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
23629
                  function($templateRequest,   $anchorScroll,   $animate,   $sce) {
23630
  return {
23631
    restrict: 'ECA',
23632
    priority: 400,
23633
    terminal: true,
23634
    transclude: 'element',
23635
    controller: angular.noop,
23636
    compile: function(element, attr) {
23637
      var srcExp = attr.ngInclude || attr.src,
23638
          onloadExp = attr.onload || '',
23639
          autoScrollExp = attr.autoscroll;
23640
 
23641
      return function(scope, $element, $attr, ctrl, $transclude) {
23642
        var changeCounter = 0,
23643
            currentScope,
23644
            previousElement,
23645
            currentElement;
23646
 
23647
        var cleanupLastIncludeContent = function() {
23648
          if (previousElement) {
23649
            previousElement.remove();
23650
            previousElement = null;
23651
          }
23652
          if (currentScope) {
23653
            currentScope.$destroy();
23654
            currentScope = null;
23655
          }
23656
          if (currentElement) {
23657
            $animate.leave(currentElement).then(function() {
23658
              previousElement = null;
23659
            });
23660
            previousElement = currentElement;
23661
            currentElement = null;
23662
          }
23663
        };
23664
 
23665
        scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
23666
          var afterAnimation = function() {
23667
            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
23668
              $anchorScroll();
23669
            }
23670
          };
23671
          var thisChangeId = ++changeCounter;
23672
 
23673
          if (src) {
23674
            //set the 2nd param to true to ignore the template request error so that the inner
23675
            //contents and scope can be cleaned up.
23676
            $templateRequest(src, true).then(function(response) {
23677
              if (thisChangeId !== changeCounter) return;
23678
              var newScope = scope.$new();
23679
              ctrl.template = response;
23680
 
23681
              // Note: This will also link all children of ng-include that were contained in the original
23682
              // html. If that content contains controllers, ... they could pollute/change the scope.
23683
              // However, using ng-include on an element with additional content does not make sense...
23684
              // Note: We can't remove them in the cloneAttchFn of $transclude as that
23685
              // function is called before linking the content, which would apply child
23686
              // directives to non existing elements.
23687
              var clone = $transclude(newScope, function(clone) {
23688
                cleanupLastIncludeContent();
23689
                $animate.enter(clone, null, $element).then(afterAnimation);
23690
              });
23691
 
23692
              currentScope = newScope;
23693
              currentElement = clone;
23694
 
23695
              currentScope.$emit('$includeContentLoaded', src);
23696
              scope.$eval(onloadExp);
23697
            }, function() {
23698
              if (thisChangeId === changeCounter) {
23699
                cleanupLastIncludeContent();
23700
                scope.$emit('$includeContentError', src);
23701
              }
23702
            });
23703
            scope.$emit('$includeContentRequested', src);
23704
          } else {
23705
            cleanupLastIncludeContent();
23706
            ctrl.template = null;
23707
          }
23708
        });
23709
      };
23710
    }
23711
  };
23712
}];
23713
 
23714
// This directive is called during the $transclude call of the first `ngInclude` directive.
23715
// It will replace and compile the content of the element with the loaded template.
23716
// We need this directive so that the element content is already filled when
23717
// the link function of another directive on the same element as ngInclude
23718
// is called.
23719
var ngIncludeFillContentDirective = ['$compile',
23720
  function($compile) {
23721
    return {
23722
      restrict: 'ECA',
23723
      priority: -400,
23724
      require: 'ngInclude',
23725
      link: function(scope, $element, $attr, ctrl) {
23726
        if (/SVG/.test($element[0].toString())) {
23727
          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
23728
          // support innerHTML, so detect this here and try to generate the contents
23729
          // specially.
23730
          $element.empty();
23731
          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
23732
              function namespaceAdaptedClone(clone) {
23733
            $element.append(clone);
23734
          }, {futureParentElement: $element});
23735
          return;
23736
        }
23737
 
23738
        $element.html(ctrl.template);
23739
        $compile($element.contents())(scope);
23740
      }
23741
    };
23742
  }];
23743
 
23744
/**
23745
 * @ngdoc directive
23746
 * @name ngInit
23747
 * @restrict AC
23748
 *
23749
 * @description
23750
 * The `ngInit` directive allows you to evaluate an expression in the
23751
 * current scope.
23752
 *
23753
 * <div class="alert alert-error">
23754
 * The only appropriate use of `ngInit` is for aliasing special properties of
23755
 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
23756
 * should use {@link guide/controller controllers} rather than `ngInit`
23757
 * to initialize values on a scope.
23758
 * </div>
23759
 * <div class="alert alert-warning">
23760
 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
23761
 * sure you have parenthesis for correct precedence:
23762
 * <pre class="prettyprint">
23763
 *   <div ng-init="test1 = (data | orderBy:'name')"></div>
23764
 * </pre>
23765
 * </div>
23766
 *
23767
 * @priority 450
23768
 *
23769
 * @element ANY
23770
 * @param {expression} ngInit {@link guide/expression Expression} to eval.
23771
 *
23772
 * @example
23773
   <example module="initExample">
23774
     <file name="index.html">
23775
   <script>
23776
     angular.module('initExample', [])
23777
       .controller('ExampleController', ['$scope', function($scope) {
23778
         $scope.list = [['a', 'b'], ['c', 'd']];
23779
       }]);
23780
   </script>
23781
   <div ng-controller="ExampleController">
23782
     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
23783
       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
23784
          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
23785
       </div>
23786
     </div>
23787
   </div>
23788
     </file>
23789
     <file name="protractor.js" type="protractor">
23790
       it('should alias index positions', function() {
23791
         var elements = element.all(by.css('.example-init'));
23792
         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
23793
         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
23794
         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
23795
         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
23796
       });
23797
     </file>
23798
   </example>
23799
 */
23800
var ngInitDirective = ngDirective({
23801
  priority: 450,
23802
  compile: function() {
23803
    return {
23804
      pre: function(scope, element, attrs) {
23805
        scope.$eval(attrs.ngInit);
23806
      }
23807
    };
23808
  }
23809
});
23810
 
23811
/**
23812
 * @ngdoc directive
23813
 * @name ngNonBindable
23814
 * @restrict AC
23815
 * @priority 1000
23816
 *
23817
 * @description
23818
 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
23819
 * DOM element. This is useful if the element contains what appears to be Angular directives and
23820
 * bindings but which should be ignored by Angular. This could be the case if you have a site that
23821
 * displays snippets of code, for instance.
23822
 *
23823
 * @element ANY
23824
 *
23825
 * @example
23826
 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
23827
 * but the one wrapped in `ngNonBindable` is left alone.
23828
 *
23829
 * @example
23830
    <example>
23831
      <file name="index.html">
23832
        <div>Normal: {{1 + 2}}</div>
23833
        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
23834
      </file>
23835
      <file name="protractor.js" type="protractor">
23836
       it('should check ng-non-bindable', function() {
23837
         expect(element(by.binding('1 + 2')).getText()).toContain('3');
23838
         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
23839
       });
23840
      </file>
23841
    </example>
23842
 */
23843
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
23844
 
23845
/**
23846
 * @ngdoc directive
23847
 * @name ngPluralize
23848
 * @restrict EA
23849
 *
23850
 * @description
23851
 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
23852
 * These rules are bundled with angular.js, but can be overridden
23853
 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
23854
 * by specifying the mappings between
23855
 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23856
 * and the strings to be displayed.
23857
 *
23858
 * # Plural categories and explicit number rules
23859
 * There are two
23860
 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23861
 * in Angular's default en-US locale: "one" and "other".
23862
 *
23863
 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
23864
 * any number that is not 1), an explicit number rule can only match one number. For example, the
23865
 * explicit number rule for "3" matches the number 3. There are examples of plural categories
23866
 * and explicit number rules throughout the rest of this documentation.
23867
 *
23868
 * # Configuring ngPluralize
23869
 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
23870
 * You can also provide an optional attribute, `offset`.
23871
 *
23872
 * The value of the `count` attribute can be either a string or an {@link guide/expression
23873
 * Angular expression}; these are evaluated on the current scope for its bound value.
23874
 *
23875
 * The `when` attribute specifies the mappings between plural categories and the actual
23876
 * string to be displayed. The value of the attribute should be a JSON object.
23877
 *
23878
 * The following example shows how to configure ngPluralize:
23879
 *
23880
 * ```html
23881
 * <ng-pluralize count="personCount"
23882
                 when="{'0': 'Nobody is viewing.',
23883
 *                      'one': '1 person is viewing.',
23884
 *                      'other': '{} people are viewing.'}">
23885
 * </ng-pluralize>
23886
 *```
23887
 *
23888
 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
23889
 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
23890
 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
23891
 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
23892
 * show "a dozen people are viewing".
23893
 *
23894
 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
23895
 * into pluralized strings. In the previous example, Angular will replace `{}` with
23896
 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
23897
 * for <span ng-non-bindable>{{numberExpression}}</span>.
23898
 *
23899
 * # Configuring ngPluralize with offset
23900
 * The `offset` attribute allows further customization of pluralized text, which can result in
23901
 * a better user experience. For example, instead of the message "4 people are viewing this document",
23902
 * you might display "John, Kate and 2 others are viewing this document".
23903
 * The offset attribute allows you to offset a number by any desired value.
23904
 * Let's take a look at an example:
23905
 *
23906
 * ```html
23907
 * <ng-pluralize count="personCount" offset=2
23908
 *               when="{'0': 'Nobody is viewing.',
23909
 *                      '1': '{{person1}} is viewing.',
23910
 *                      '2': '{{person1}} and {{person2}} are viewing.',
23911
 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
23912
 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23913
 * </ng-pluralize>
23914
 * ```
23915
 *
23916
 * Notice that we are still using two plural categories(one, other), but we added
23917
 * three explicit number rules 0, 1 and 2.
23918
 * When one person, perhaps John, views the document, "John is viewing" will be shown.
23919
 * When three people view the document, no explicit number rule is found, so
23920
 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
23921
 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
23922
 * is shown.
23923
 *
23924
 * Note that when you specify offsets, you must provide explicit number rules for
23925
 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
23926
 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
23927
 * plural categories "one" and "other".
23928
 *
23929
 * @param {string|expression} count The variable to be bound to.
23930
 * @param {string} when The mapping between plural category to its corresponding strings.
23931
 * @param {number=} offset Offset to deduct from the total number.
23932
 *
23933
 * @example
23934
    <example module="pluralizeExample">
23935
      <file name="index.html">
23936
        <script>
23937
          angular.module('pluralizeExample', [])
23938
            .controller('ExampleController', ['$scope', function($scope) {
23939
              $scope.person1 = 'Igor';
23940
              $scope.person2 = 'Misko';
23941
              $scope.personCount = 1;
23942
            }]);
23943
        </script>
23944
        <div ng-controller="ExampleController">
23945
          Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
23946
          Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
23947
          Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
23948
 
23949
          <!--- Example with simple pluralization rules for en locale --->
23950
          Without Offset:
23951
          <ng-pluralize count="personCount"
23952
                        when="{'0': 'Nobody is viewing.',
23953
                               'one': '1 person is viewing.',
23954
                               'other': '{} people are viewing.'}">
23955
          </ng-pluralize><br>
23956
 
23957
          <!--- Example with offset --->
23958
          With Offset(2):
23959
          <ng-pluralize count="personCount" offset=2
23960
                        when="{'0': 'Nobody is viewing.',
23961
                               '1': '{{person1}} is viewing.',
23962
                               '2': '{{person1}} and {{person2}} are viewing.',
23963
                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
23964
                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23965
          </ng-pluralize>
23966
        </div>
23967
      </file>
23968
      <file name="protractor.js" type="protractor">
23969
        it('should show correct pluralized string', function() {
23970
          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
23971
          var withOffset = element.all(by.css('ng-pluralize')).get(1);
23972
          var countInput = element(by.model('personCount'));
23973
 
23974
          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
23975
          expect(withOffset.getText()).toEqual('Igor is viewing.');
23976
 
23977
          countInput.clear();
23978
          countInput.sendKeys('0');
23979
 
23980
          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
23981
          expect(withOffset.getText()).toEqual('Nobody is viewing.');
23982
 
23983
          countInput.clear();
23984
          countInput.sendKeys('2');
23985
 
23986
          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
23987
          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
23988
 
23989
          countInput.clear();
23990
          countInput.sendKeys('3');
23991
 
23992
          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
23993
          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
23994
 
23995
          countInput.clear();
23996
          countInput.sendKeys('4');
23997
 
23998
          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
23999
          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
24000
        });
24001
        it('should show data-bound names', function() {
24002
          var withOffset = element.all(by.css('ng-pluralize')).get(1);
24003
          var personCount = element(by.model('personCount'));
24004
          var person1 = element(by.model('person1'));
24005
          var person2 = element(by.model('person2'));
24006
          personCount.clear();
24007
          personCount.sendKeys('4');
24008
          person1.clear();
24009
          person1.sendKeys('Di');
24010
          person2.clear();
24011
          person2.sendKeys('Vojta');
24012
          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
24013
        });
24014
      </file>
24015
    </example>
24016
 */
24017
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
24018
  var BRACE = /{}/g,
24019
      IS_WHEN = /^when(Minus)?(.+)$/;
24020
 
24021
  return {
24022
    restrict: 'EA',
24023
    link: function(scope, element, attr) {
24024
      var numberExp = attr.count,
24025
          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
24026
          offset = attr.offset || 0,
24027
          whens = scope.$eval(whenExp) || {},
24028
          whensExpFns = {},
24029
          startSymbol = $interpolate.startSymbol(),
24030
          endSymbol = $interpolate.endSymbol(),
24031
          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
24032
          watchRemover = angular.noop,
24033
          lastCount;
24034
 
24035
      forEach(attr, function(expression, attributeName) {
24036
        var tmpMatch = IS_WHEN.exec(attributeName);
24037
        if (tmpMatch) {
24038
          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
24039
          whens[whenKey] = element.attr(attr.$attr[attributeName]);
24040
        }
24041
      });
24042
      forEach(whens, function(expression, key) {
24043
        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
24044
 
24045
      });
24046
 
24047
      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
24048
        var count = parseFloat(newVal);
24049
        var countIsNaN = isNaN(count);
24050
 
24051
        if (!countIsNaN && !(count in whens)) {
24052
          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
24053
          // Otherwise, check it against pluralization rules in $locale service.
24054
          count = $locale.pluralCat(count - offset);
24055
        }
24056
 
24057
        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
24058
        // In JS `NaN !== NaN`, so we have to exlicitly check.
24059
        if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
24060
          watchRemover();
24061
          watchRemover = scope.$watch(whensExpFns[count], updateElementText);
24062
          lastCount = count;
24063
        }
24064
      });
24065
 
24066
      function updateElementText(newText) {
24067
        element.text(newText || '');
24068
      }
24069
    }
24070
  };
24071
}];
24072
 
24073
/**
24074
 * @ngdoc directive
24075
 * @name ngRepeat
24076
 *
24077
 * @description
24078
 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
24079
 * instance gets its own scope, where the given loop variable is set to the current collection item,
24080
 * and `$index` is set to the item index or key.
24081
 *
24082
 * Special properties are exposed on the local scope of each template instance, including:
24083
 *
24084
 * | Variable  | Type            | Details                                                                     |
24085
 * |-----------|-----------------|-----------------------------------------------------------------------------|
24086
 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
24087
 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
24088
 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
24089
 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
24090
 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
24091
 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
24092
 *
24093
 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
24094
 * This may be useful when, for instance, nesting ngRepeats.
24095
 *
24096
 * # Special repeat start and end points
24097
 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
24098
 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
24099
 * 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)
24100
 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
24101
 *
24102
 * The example below makes use of this feature:
24103
 * ```html
24104
 *   <header ng-repeat-start="item in items">
24105
 *     Header {{ item }}
24106
 *   </header>
24107
 *   <div class="body">
24108
 *     Body {{ item }}
24109
 *   </div>
24110
 *   <footer ng-repeat-end>
24111
 *     Footer {{ item }}
24112
 *   </footer>
24113
 * ```
24114
 *
24115
 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
24116
 * ```html
24117
 *   <header>
24118
 *     Header A
24119
 *   </header>
24120
 *   <div class="body">
24121
 *     Body A
24122
 *   </div>
24123
 *   <footer>
24124
 *     Footer A
24125
 *   </footer>
24126
 *   <header>
24127
 *     Header B
24128
 *   </header>
24129
 *   <div class="body">
24130
 *     Body B
24131
 *   </div>
24132
 *   <footer>
24133
 *     Footer B
24134
 *   </footer>
24135
 * ```
24136
 *
24137
 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
24138
 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
24139
 *
24140
 * @animations
24141
 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
24142
 *
24143
 * **.leave** - when an item is removed from the list or when an item is filtered out
24144
 *
24145
 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
24146
 *
24147
 * @element ANY
24148
 * @scope
24149
 * @priority 1000
24150
 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
24151
 *   formats are currently supported:
24152
 *
24153
 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
24154
 *     is a scope expression giving the collection to enumerate.
24155
 *
24156
 *     For example: `album in artist.albums`.
24157
 *
24158
 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
24159
 *     and `expression` is the scope expression giving the collection to enumerate.
24160
 *
24161
 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
24162
 *
24163
 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
24164
 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking function
24165
 *     is specified the ng-repeat associates elements by identity in the collection. It is an error to have
24166
 *     more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
24167
 *     mapped to the same DOM element, which is not possible.)  Filters should be applied to the expression,
24168
 *     before specifying a tracking expression.
24169
 *
24170
 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
24171
 *     will be associated by item identity in the array.
24172
 *
24173
 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
24174
 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
24175
 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
24176
 *     element in the same way in the DOM.
24177
 *
24178
 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
24179
 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
24180
 *     property is same.
24181
 *
24182
 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
24183
 *     to items in conjunction with a tracking expression.
24184
 *
24185
 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
24186
 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
24187
 *     when a filter is active on the repeater, but the filtered result set is empty.
24188
 *
24189
 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
24190
 *     the items have been processed through the filter.
24191
 *
24192
 * @example
24193
 * This example initializes the scope to a list of names and
24194
 * then uses `ngRepeat` to display every person:
24195
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24196
    <file name="index.html">
24197
      <div ng-init="friends = [
24198
        {name:'John', age:25, gender:'boy'},
24199
        {name:'Jessie', age:30, gender:'girl'},
24200
        {name:'Johanna', age:28, gender:'girl'},
24201
        {name:'Joy', age:15, gender:'girl'},
24202
        {name:'Mary', age:28, gender:'girl'},
24203
        {name:'Peter', age:95, gender:'boy'},
24204
        {name:'Sebastian', age:50, gender:'boy'},
24205
        {name:'Erika', age:27, gender:'girl'},
24206
        {name:'Patrick', age:40, gender:'boy'},
24207
        {name:'Samantha', age:60, gender:'girl'}
24208
      ]">
24209
        I have {{friends.length}} friends. They are:
24210
        <input type="search" ng-model="q" placeholder="filter friends..." />
24211
        <ul class="example-animate-container">
24212
          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
24213
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
24214
          </li>
24215
          <li class="animate-repeat" ng-if="results.length == 0">
24216
            <strong>No results found...</strong>
24217
          </li>
24218
        </ul>
24219
      </div>
24220
    </file>
24221
    <file name="animations.css">
24222
      .example-animate-container {
24223
        background:white;
24224
        border:1px solid black;
24225
        list-style:none;
24226
        margin:0;
24227
        padding:0 10px;
24228
      }
24229
 
24230
      .animate-repeat {
24231
        line-height:40px;
24232
        list-style:none;
24233
        box-sizing:border-box;
24234
      }
24235
 
24236
      .animate-repeat.ng-move,
24237
      .animate-repeat.ng-enter,
24238
      .animate-repeat.ng-leave {
24239
        -webkit-transition:all linear 0.5s;
24240
        transition:all linear 0.5s;
24241
      }
24242
 
24243
      .animate-repeat.ng-leave.ng-leave-active,
24244
      .animate-repeat.ng-move,
24245
      .animate-repeat.ng-enter {
24246
        opacity:0;
24247
        max-height:0;
24248
      }
24249
 
24250
      .animate-repeat.ng-leave,
24251
      .animate-repeat.ng-move.ng-move-active,
24252
      .animate-repeat.ng-enter.ng-enter-active {
24253
        opacity:1;
24254
        max-height:40px;
24255
      }
24256
    </file>
24257
    <file name="protractor.js" type="protractor">
24258
      var friends = element.all(by.repeater('friend in friends'));
24259
 
24260
      it('should render initial data set', function() {
24261
        expect(friends.count()).toBe(10);
24262
        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
24263
        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
24264
        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
24265
        expect(element(by.binding('friends.length')).getText())
24266
            .toMatch("I have 10 friends. They are:");
24267
      });
24268
 
24269
       it('should update repeater when filter predicate changes', function() {
24270
         expect(friends.count()).toBe(10);
24271
 
24272
         element(by.model('q')).sendKeys('ma');
24273
 
24274
         expect(friends.count()).toBe(2);
24275
         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
24276
         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
24277
       });
24278
      </file>
24279
    </example>
24280
 */
24281
var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24282
  var NG_REMOVED = '$$NG_REMOVED';
24283
  var ngRepeatMinErr = minErr('ngRepeat');
24284
 
24285
  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
24286
    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
24287
    scope[valueIdentifier] = value;
24288
    if (keyIdentifier) scope[keyIdentifier] = key;
24289
    scope.$index = index;
24290
    scope.$first = (index === 0);
24291
    scope.$last = (index === (arrayLength - 1));
24292
    scope.$middle = !(scope.$first || scope.$last);
24293
    // jshint bitwise: false
24294
    scope.$odd = !(scope.$even = (index&1) === 0);
24295
    // jshint bitwise: true
24296
  };
24297
 
24298
  var getBlockStart = function(block) {
24299
    return block.clone[0];
24300
  };
24301
 
24302
  var getBlockEnd = function(block) {
24303
    return block.clone[block.clone.length - 1];
24304
  };
24305
 
24306
 
24307
  return {
24308
    restrict: 'A',
24309
    multiElement: true,
24310
    transclude: 'element',
24311
    priority: 1000,
24312
    terminal: true,
24313
    $$tlb: true,
24314
    compile: function ngRepeatCompile($element, $attr) {
24315
      var expression = $attr.ngRepeat;
24316
      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
24317
 
24318
      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*$/);
24319
 
24320
      if (!match) {
24321
        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
24322
            expression);
24323
      }
24324
 
24325
      var lhs = match[1];
24326
      var rhs = match[2];
24327
      var aliasAs = match[3];
24328
      var trackByExp = match[4];
24329
 
24330
      match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
24331
 
24332
      if (!match) {
24333
        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
24334
            lhs);
24335
      }
24336
      var valueIdentifier = match[3] || match[1];
24337
      var keyIdentifier = match[2];
24338
 
24339
      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
24340
          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent)$/.test(aliasAs))) {
24341
        throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
24342
          aliasAs);
24343
      }
24344
 
24345
      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
24346
      var hashFnLocals = {$id: hashKey};
24347
 
24348
      if (trackByExp) {
24349
        trackByExpGetter = $parse(trackByExp);
24350
      } else {
24351
        trackByIdArrayFn = function(key, value) {
24352
          return hashKey(value);
24353
        };
24354
        trackByIdObjFn = function(key) {
24355
          return key;
24356
        };
24357
      }
24358
 
24359
      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
24360
 
24361
        if (trackByExpGetter) {
24362
          trackByIdExpFn = function(key, value, index) {
24363
            // assign key, value, and $index to the locals so that they can be used in hash functions
24364
            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24365
            hashFnLocals[valueIdentifier] = value;
24366
            hashFnLocals.$index = index;
24367
            return trackByExpGetter($scope, hashFnLocals);
24368
          };
24369
        }
24370
 
24371
        // Store a list of elements from previous run. This is a hash where key is the item from the
24372
        // iterator, and the value is objects with following properties.
24373
        //   - scope: bound scope
24374
        //   - element: previous element.
24375
        //   - index: position
24376
        //
24377
        // We are using no-proto object so that we don't need to guard against inherited props via
24378
        // hasOwnProperty.
24379
        var lastBlockMap = createMap();
24380
 
24381
        //watch props
24382
        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
24383
          var index, length,
24384
              previousNode = $element[0],     // node that cloned nodes should be inserted after
24385
                                              // initialized to the comment node anchor
24386
              nextNode,
24387
              // Same as lastBlockMap but it has the current state. It will become the
24388
              // lastBlockMap on the next iteration.
24389
              nextBlockMap = createMap(),
24390
              collectionLength,
24391
              key, value, // key/value of iteration
24392
              trackById,
24393
              trackByIdFn,
24394
              collectionKeys,
24395
              block,       // last object information {scope, element, id}
24396
              nextBlockOrder,
24397
              elementsToRemove;
24398
 
24399
          if (aliasAs) {
24400
            $scope[aliasAs] = collection;
24401
          }
24402
 
24403
          if (isArrayLike(collection)) {
24404
            collectionKeys = collection;
24405
            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
24406
          } else {
24407
            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
24408
            // if object, extract keys, sort them and use to determine order of iteration over obj props
24409
            collectionKeys = [];
24410
            for (var itemKey in collection) {
24411
              if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
24412
                collectionKeys.push(itemKey);
24413
              }
24414
            }
24415
            collectionKeys.sort();
24416
          }
24417
 
24418
          collectionLength = collectionKeys.length;
24419
          nextBlockOrder = new Array(collectionLength);
24420
 
24421
          // locate existing items
24422
          for (index = 0; index < collectionLength; index++) {
24423
            key = (collection === collectionKeys) ? index : collectionKeys[index];
24424
            value = collection[key];
24425
            trackById = trackByIdFn(key, value, index);
24426
            if (lastBlockMap[trackById]) {
24427
              // found previously seen block
24428
              block = lastBlockMap[trackById];
24429
              delete lastBlockMap[trackById];
24430
              nextBlockMap[trackById] = block;
24431
              nextBlockOrder[index] = block;
24432
            } else if (nextBlockMap[trackById]) {
24433
              // if collision detected. restore lastBlockMap and throw an error
24434
              forEach(nextBlockOrder, function(block) {
24435
                if (block && block.scope) lastBlockMap[block.id] = block;
24436
              });
24437
              throw ngRepeatMinErr('dupes',
24438
                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24439
                  expression, trackById, value);
24440
            } else {
24441
              // new never before seen block
24442
              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
24443
              nextBlockMap[trackById] = true;
24444
            }
24445
          }
24446
 
24447
          // remove leftover items
24448
          for (var blockKey in lastBlockMap) {
24449
            block = lastBlockMap[blockKey];
24450
            elementsToRemove = getBlockNodes(block.clone);
24451
            $animate.leave(elementsToRemove);
24452
            if (elementsToRemove[0].parentNode) {
24453
              // if the element was not removed yet because of pending animation, mark it as deleted
24454
              // so that we can ignore it later
24455
              for (index = 0, length = elementsToRemove.length; index < length; index++) {
24456
                elementsToRemove[index][NG_REMOVED] = true;
24457
              }
24458
            }
24459
            block.scope.$destroy();
24460
          }
24461
 
24462
          // we are not using forEach for perf reasons (trying to avoid #call)
24463
          for (index = 0; index < collectionLength; index++) {
24464
            key = (collection === collectionKeys) ? index : collectionKeys[index];
24465
            value = collection[key];
24466
            block = nextBlockOrder[index];
24467
 
24468
            if (block.scope) {
24469
              // if we have already seen this object, then we need to reuse the
24470
              // associated scope/element
24471
 
24472
              nextNode = previousNode;
24473
 
24474
              // skip nodes that are already pending removal via leave animation
24475
              do {
24476
                nextNode = nextNode.nextSibling;
24477
              } while (nextNode && nextNode[NG_REMOVED]);
24478
 
24479
              if (getBlockStart(block) != nextNode) {
24480
                // existing item which got moved
24481
                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
24482
              }
24483
              previousNode = getBlockEnd(block);
24484
              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24485
            } else {
24486
              // new item which we don't know about
24487
              $transclude(function ngRepeatTransclude(clone, scope) {
24488
                block.scope = scope;
24489
                // http://jsperf.com/clone-vs-createcomment
24490
                var endNode = ngRepeatEndComment.cloneNode(false);
24491
                clone[clone.length++] = endNode;
24492
 
24493
                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
24494
                $animate.enter(clone, null, jqLite(previousNode));
24495
                previousNode = endNode;
24496
                // Note: We only need the first/last node of the cloned nodes.
24497
                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24498
                // by a directive with templateUrl when its template arrives.
24499
                block.clone = clone;
24500
                nextBlockMap[block.id] = block;
24501
                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24502
              });
24503
            }
24504
          }
24505
          lastBlockMap = nextBlockMap;
24506
        });
24507
      };
24508
    }
24509
  };
24510
}];
24511
 
24512
var NG_HIDE_CLASS = 'ng-hide';
24513
var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24514
/**
24515
 * @ngdoc directive
24516
 * @name ngShow
24517
 *
24518
 * @description
24519
 * The `ngShow` directive shows or hides the given HTML element based on the expression
24520
 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
24521
 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24522
 * in AngularJS and sets the display style to none (using an !important flag).
24523
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24524
 *
24525
 * ```html
24526
 * <!-- when $scope.myValue is truthy (element is visible) -->
24527
 * <div ng-show="myValue"></div>
24528
 *
24529
 * <!-- when $scope.myValue is falsy (element is hidden) -->
24530
 * <div ng-show="myValue" class="ng-hide"></div>
24531
 * ```
24532
 *
24533
 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
24534
 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
24535
 * from the element causing the element not to appear hidden.
24536
 *
24537
 * ## Why is !important used?
24538
 *
24539
 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24540
 * can be easily overridden by heavier selectors. For example, something as simple
24541
 * as changing the display style on a HTML list item would make hidden elements appear visible.
24542
 * This also becomes a bigger issue when dealing with CSS frameworks.
24543
 *
24544
 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24545
 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24546
 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24547
 *
24548
 * ### Overriding `.ng-hide`
24549
 *
24550
 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24551
 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24552
 * class in CSS:
24553
 *
24554
 * ```css
24555
 * .ng-hide {
24556
 *   /&#42; this is just another form of hiding an element &#42;/
24557
 *   display: block!important;
24558
 *   position: absolute;
24559
 *   top: -9999px;
24560
 *   left: -9999px;
24561
 * }
24562
 * ```
24563
 *
24564
 * By default you don't need to override in CSS anything and the animations will work around the display style.
24565
 *
24566
 * ## A note about animations with `ngShow`
24567
 *
24568
 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24569
 * is true and false. This system works like the animation system present with ngClass except that
24570
 * you must also include the !important flag to override the display property
24571
 * so that you can perform an animation when the element is hidden during the time of the animation.
24572
 *
24573
 * ```css
24574
 * //
24575
 * //a working example can be found at the bottom of this page
24576
 * //
24577
 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24578
 *   /&#42; this is required as of 1.3x to properly
24579
 *      apply all styling in a show/hide animation &#42;/
24580
 *   transition: 0s linear all;
24581
 * }
24582
 *
24583
 * .my-element.ng-hide-add-active,
24584
 * .my-element.ng-hide-remove-active {
24585
 *   /&#42; the transition is defined in the active class &#42;/
24586
 *   transition: 1s linear all;
24587
 * }
24588
 *
24589
 * .my-element.ng-hide-add { ... }
24590
 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24591
 * .my-element.ng-hide-remove { ... }
24592
 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24593
 * ```
24594
 *
24595
 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24596
 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24597
 *
24598
 * @animations
24599
 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
24600
 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
24601
 *
24602
 * @element ANY
24603
 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
24604
 *     then the element is shown or hidden respectively.
24605
 *
24606
 * @example
24607
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24608
    <file name="index.html">
24609
      Click me: <input type="checkbox" ng-model="checked"><br/>
24610
      <div>
24611
        Show:
24612
        <div class="check-element animate-show" ng-show="checked">
24613
          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24614
        </div>
24615
      </div>
24616
      <div>
24617
        Hide:
24618
        <div class="check-element animate-show" ng-hide="checked">
24619
          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24620
        </div>
24621
      </div>
24622
    </file>
24623
    <file name="glyphicons.css">
24624
      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24625
    </file>
24626
    <file name="animations.css">
24627
      .animate-show {
24628
        line-height: 20px;
24629
        opacity: 1;
24630
        padding: 10px;
24631
        border: 1px solid black;
24632
        background: white;
24633
      }
24634
 
24635
      .animate-show.ng-hide-add.ng-hide-add-active,
24636
      .animate-show.ng-hide-remove.ng-hide-remove-active {
24637
        -webkit-transition: all linear 0.5s;
24638
        transition: all linear 0.5s;
24639
      }
24640
 
24641
      .animate-show.ng-hide {
24642
        line-height: 0;
24643
        opacity: 0;
24644
        padding: 0 10px;
24645
      }
24646
 
24647
      .check-element {
24648
        padding: 10px;
24649
        border: 1px solid black;
24650
        background: white;
24651
      }
24652
    </file>
24653
    <file name="protractor.js" type="protractor">
24654
      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24655
      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24656
 
24657
      it('should check ng-show / ng-hide', function() {
24658
        expect(thumbsUp.isDisplayed()).toBeFalsy();
24659
        expect(thumbsDown.isDisplayed()).toBeTruthy();
24660
 
24661
        element(by.model('checked')).click();
24662
 
24663
        expect(thumbsUp.isDisplayed()).toBeTruthy();
24664
        expect(thumbsDown.isDisplayed()).toBeFalsy();
24665
      });
24666
    </file>
24667
  </example>
24668
 */
24669
var ngShowDirective = ['$animate', function($animate) {
24670
  return {
24671
    restrict: 'A',
24672
    multiElement: true,
24673
    link: function(scope, element, attr) {
24674
      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
24675
        // we're adding a temporary, animation-specific class for ng-hide since this way
24676
        // we can control when the element is actually displayed on screen without having
24677
        // to have a global/greedy CSS selector that breaks when other animations are run.
24678
        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
24679
        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
24680
          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24681
        });
24682
      });
24683
    }
24684
  };
24685
}];
24686
 
24687
 
24688
/**
24689
 * @ngdoc directive
24690
 * @name ngHide
24691
 *
24692
 * @description
24693
 * The `ngHide` directive shows or hides the given HTML element based on the expression
24694
 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
24695
 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24696
 * in AngularJS and sets the display style to none (using an !important flag).
24697
 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24698
 *
24699
 * ```html
24700
 * <!-- when $scope.myValue is truthy (element is hidden) -->
24701
 * <div ng-hide="myValue" class="ng-hide"></div>
24702
 *
24703
 * <!-- when $scope.myValue is falsy (element is visible) -->
24704
 * <div ng-hide="myValue"></div>
24705
 * ```
24706
 *
24707
 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
24708
 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
24709
 * from the element causing the element not to appear hidden.
24710
 *
24711
 * ## Why is !important used?
24712
 *
24713
 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24714
 * can be easily overridden by heavier selectors. For example, something as simple
24715
 * as changing the display style on a HTML list item would make hidden elements appear visible.
24716
 * This also becomes a bigger issue when dealing with CSS frameworks.
24717
 *
24718
 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24719
 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24720
 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24721
 *
24722
 * ### Overriding `.ng-hide`
24723
 *
24724
 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24725
 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24726
 * class in CSS:
24727
 *
24728
 * ```css
24729
 * .ng-hide {
24730
 *   /&#42; this is just another form of hiding an element &#42;/
24731
 *   display: block!important;
24732
 *   position: absolute;
24733
 *   top: -9999px;
24734
 *   left: -9999px;
24735
 * }
24736
 * ```
24737
 *
24738
 * By default you don't need to override in CSS anything and the animations will work around the display style.
24739
 *
24740
 * ## A note about animations with `ngHide`
24741
 *
24742
 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24743
 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
24744
 * CSS class is added and removed for you instead of your own CSS class.
24745
 *
24746
 * ```css
24747
 * //
24748
 * //a working example can be found at the bottom of this page
24749
 * //
24750
 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24751
 *   transition: 0.5s linear all;
24752
 * }
24753
 *
24754
 * .my-element.ng-hide-add { ... }
24755
 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24756
 * .my-element.ng-hide-remove { ... }
24757
 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24758
 * ```
24759
 *
24760
 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24761
 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24762
 *
24763
 * @animations
24764
 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
24765
 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
24766
 *
24767
 * @element ANY
24768
 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
24769
 *     the element is shown or hidden respectively.
24770
 *
24771
 * @example
24772
  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24773
    <file name="index.html">
24774
      Click me: <input type="checkbox" ng-model="checked"><br/>
24775
      <div>
24776
        Show:
24777
        <div class="check-element animate-hide" ng-show="checked">
24778
          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24779
        </div>
24780
      </div>
24781
      <div>
24782
        Hide:
24783
        <div class="check-element animate-hide" ng-hide="checked">
24784
          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24785
        </div>
24786
      </div>
24787
    </file>
24788
    <file name="glyphicons.css">
24789
      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24790
    </file>
24791
    <file name="animations.css">
24792
      .animate-hide {
24793
        -webkit-transition: all linear 0.5s;
24794
        transition: all linear 0.5s;
24795
        line-height: 20px;
24796
        opacity: 1;
24797
        padding: 10px;
24798
        border: 1px solid black;
24799
        background: white;
24800
      }
24801
 
24802
      .animate-hide.ng-hide {
24803
        line-height: 0;
24804
        opacity: 0;
24805
        padding: 0 10px;
24806
      }
24807
 
24808
      .check-element {
24809
        padding: 10px;
24810
        border: 1px solid black;
24811
        background: white;
24812
      }
24813
    </file>
24814
    <file name="protractor.js" type="protractor">
24815
      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24816
      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24817
 
24818
      it('should check ng-show / ng-hide', function() {
24819
        expect(thumbsUp.isDisplayed()).toBeFalsy();
24820
        expect(thumbsDown.isDisplayed()).toBeTruthy();
24821
 
24822
        element(by.model('checked')).click();
24823
 
24824
        expect(thumbsUp.isDisplayed()).toBeTruthy();
24825
        expect(thumbsDown.isDisplayed()).toBeFalsy();
24826
      });
24827
    </file>
24828
  </example>
24829
 */
24830
var ngHideDirective = ['$animate', function($animate) {
24831
  return {
24832
    restrict: 'A',
24833
    multiElement: true,
24834
    link: function(scope, element, attr) {
24835
      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
24836
        // The comment inside of the ngShowDirective explains why we add and
24837
        // remove a temporary class for the show/hide animation
24838
        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
24839
          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24840
        });
24841
      });
24842
    }
24843
  };
24844
}];
24845
 
24846
/**
24847
 * @ngdoc directive
24848
 * @name ngStyle
24849
 * @restrict AC
24850
 *
24851
 * @description
24852
 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
24853
 *
24854
 * @element ANY
24855
 * @param {expression} ngStyle
24856
 *
24857
 * {@link guide/expression Expression} which evals to an
24858
 * object whose keys are CSS style names and values are corresponding values for those CSS
24859
 * keys.
24860
 *
24861
 * Since some CSS style names are not valid keys for an object, they must be quoted.
24862
 * See the 'background-color' style in the example below.
24863
 *
24864
 * @example
24865
   <example>
24866
     <file name="index.html">
24867
        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
24868
        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
24869
        <input type="button" value="clear" ng-click="myStyle={}">
24870
        <br/>
24871
        <span ng-style="myStyle">Sample Text</span>
24872
        <pre>myStyle={{myStyle}}</pre>
24873
     </file>
24874
     <file name="style.css">
24875
       span {
24876
         color: black;
24877
       }
24878
     </file>
24879
     <file name="protractor.js" type="protractor">
24880
       var colorSpan = element(by.css('span'));
24881
 
24882
       it('should check ng-style', function() {
24883
         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24884
         element(by.css('input[value=\'set color\']')).click();
24885
         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
24886
         element(by.css('input[value=clear]')).click();
24887
         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24888
       });
24889
     </file>
24890
   </example>
24891
 */
24892
var ngStyleDirective = ngDirective(function(scope, element, attr) {
24893
  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
24894
    if (oldStyles && (newStyles !== oldStyles)) {
24895
      forEach(oldStyles, function(val, style) { element.css(style, '');});
24896
    }
24897
    if (newStyles) element.css(newStyles);
24898
  }, true);
24899
});
24900
 
24901
/**
24902
 * @ngdoc directive
24903
 * @name ngSwitch
24904
 * @restrict EA
24905
 *
24906
 * @description
24907
 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
24908
 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
24909
 * as specified in the template.
24910
 *
24911
 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
24912
 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
24913
 * matches the value obtained from the evaluated expression. In other words, you define a container element
24914
 * (where you place the directive), place an expression on the **`on="..."` attribute**
24915
 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
24916
 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
24917
 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
24918
 * attribute is displayed.
24919
 *
24920
 * <div class="alert alert-info">
24921
 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
24922
 * as literal string values to match against.
24923
 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
24924
 * value of the expression `$scope.someVal`.
24925
 * </div>
24926
 
24927
 * @animations
24928
 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
24929
 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
24930
 *
24931
 * @usage
24932
 *
24933
 * ```
24934
 * <ANY ng-switch="expression">
24935
 *   <ANY ng-switch-when="matchValue1">...</ANY>
24936
 *   <ANY ng-switch-when="matchValue2">...</ANY>
24937
 *   <ANY ng-switch-default>...</ANY>
24938
 * </ANY>
24939
 * ```
24940
 *
24941
 *
24942
 * @scope
24943
 * @priority 1200
24944
 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
24945
 * On child elements add:
24946
 *
24947
 * * `ngSwitchWhen`: the case statement to match against. If match then this
24948
 *   case will be displayed. If the same match appears multiple times, all the
24949
 *   elements will be displayed.
24950
 * * `ngSwitchDefault`: the default case when no other case match. If there
24951
 *   are multiple default cases, all of them will be displayed when no other
24952
 *   case match.
24953
 *
24954
 *
24955
 * @example
24956
  <example module="switchExample" deps="angular-animate.js" animations="true">
24957
    <file name="index.html">
24958
      <div ng-controller="ExampleController">
24959
        <select ng-model="selection" ng-options="item for item in items">
24960
        </select>
24961
        <tt>selection={{selection}}</tt>
24962
        <hr/>
24963
        <div class="animate-switch-container"
24964
          ng-switch on="selection">
24965
            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
24966
            <div class="animate-switch" ng-switch-when="home">Home Span</div>
24967
            <div class="animate-switch" ng-switch-default>default</div>
24968
        </div>
24969
      </div>
24970
    </file>
24971
    <file name="script.js">
24972
      angular.module('switchExample', ['ngAnimate'])
24973
        .controller('ExampleController', ['$scope', function($scope) {
24974
          $scope.items = ['settings', 'home', 'other'];
24975
          $scope.selection = $scope.items[0];
24976
        }]);
24977
    </file>
24978
    <file name="animations.css">
24979
      .animate-switch-container {
24980
        position:relative;
24981
        background:white;
24982
        border:1px solid black;
24983
        height:40px;
24984
        overflow:hidden;
24985
      }
24986
 
24987
      .animate-switch {
24988
        padding:10px;
24989
      }
24990
 
24991
      .animate-switch.ng-animate {
24992
        -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24993
        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24994
 
24995
        position:absolute;
24996
        top:0;
24997
        left:0;
24998
        right:0;
24999
        bottom:0;
25000
      }
25001
 
25002
      .animate-switch.ng-leave.ng-leave-active,
25003
      .animate-switch.ng-enter {
25004
        top:-50px;
25005
      }
25006
      .animate-switch.ng-leave,
25007
      .animate-switch.ng-enter.ng-enter-active {
25008
        top:0;
25009
      }
25010
    </file>
25011
    <file name="protractor.js" type="protractor">
25012
      var switchElem = element(by.css('[ng-switch]'));
25013
      var select = element(by.model('selection'));
25014
 
25015
      it('should start in settings', function() {
25016
        expect(switchElem.getText()).toMatch(/Settings Div/);
25017
      });
25018
      it('should change to home', function() {
25019
        select.all(by.css('option')).get(1).click();
25020
        expect(switchElem.getText()).toMatch(/Home Span/);
25021
      });
25022
      it('should select default', function() {
25023
        select.all(by.css('option')).get(2).click();
25024
        expect(switchElem.getText()).toMatch(/default/);
25025
      });
25026
    </file>
25027
  </example>
25028
 */
25029
var ngSwitchDirective = ['$animate', function($animate) {
25030
  return {
25031
    restrict: 'EA',
25032
    require: 'ngSwitch',
25033
 
25034
    // asks for $scope to fool the BC controller module
25035
    controller: ['$scope', function ngSwitchController() {
25036
     this.cases = {};
25037
    }],
25038
    link: function(scope, element, attr, ngSwitchController) {
25039
      var watchExpr = attr.ngSwitch || attr.on,
25040
          selectedTranscludes = [],
25041
          selectedElements = [],
25042
          previousLeaveAnimations = [],
25043
          selectedScopes = [];
25044
 
25045
      var spliceFactory = function(array, index) {
25046
          return function() { array.splice(index, 1); };
25047
      };
25048
 
25049
      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
25050
        var i, ii;
25051
        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
25052
          $animate.cancel(previousLeaveAnimations[i]);
25053
        }
25054
        previousLeaveAnimations.length = 0;
25055
 
25056
        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
25057
          var selected = getBlockNodes(selectedElements[i].clone);
25058
          selectedScopes[i].$destroy();
25059
          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
25060
          promise.then(spliceFactory(previousLeaveAnimations, i));
25061
        }
25062
 
25063
        selectedElements.length = 0;
25064
        selectedScopes.length = 0;
25065
 
25066
        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
25067
          forEach(selectedTranscludes, function(selectedTransclude) {
25068
            selectedTransclude.transclude(function(caseElement, selectedScope) {
25069
              selectedScopes.push(selectedScope);
25070
              var anchor = selectedTransclude.element;
25071
              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
25072
              var block = { clone: caseElement };
25073
 
25074
              selectedElements.push(block);
25075
              $animate.enter(caseElement, anchor.parent(), anchor);
25076
            });
25077
          });
25078
        }
25079
      });
25080
    }
25081
  };
25082
}];
25083
 
25084
var ngSwitchWhenDirective = ngDirective({
25085
  transclude: 'element',
25086
  priority: 1200,
25087
  require: '^ngSwitch',
25088
  multiElement: true,
25089
  link: function(scope, element, attrs, ctrl, $transclude) {
25090
    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
25091
    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
25092
  }
25093
});
25094
 
25095
var ngSwitchDefaultDirective = ngDirective({
25096
  transclude: 'element',
25097
  priority: 1200,
25098
  require: '^ngSwitch',
25099
  multiElement: true,
25100
  link: function(scope, element, attr, ctrl, $transclude) {
25101
    ctrl.cases['?'] = (ctrl.cases['?'] || []);
25102
    ctrl.cases['?'].push({ transclude: $transclude, element: element });
25103
   }
25104
});
25105
 
25106
/**
25107
 * @ngdoc directive
25108
 * @name ngTransclude
25109
 * @restrict EAC
25110
 *
25111
 * @description
25112
 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
25113
 *
25114
 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
25115
 *
25116
 * @element ANY
25117
 *
25118
 * @example
25119
   <example module="transcludeExample">
25120
     <file name="index.html">
25121
       <script>
25122
         angular.module('transcludeExample', [])
25123
          .directive('pane', function(){
25124
             return {
25125
               restrict: 'E',
25126
               transclude: true,
25127
               scope: { title:'@' },
25128
               template: '<div style="border: 1px solid black;">' +
25129
                           '<div style="background-color: gray">{{title}}</div>' +
25130
                           '<ng-transclude></ng-transclude>' +
25131
                         '</div>'
25132
             };
25133
         })
25134
         .controller('ExampleController', ['$scope', function($scope) {
25135
           $scope.title = 'Lorem Ipsum';
25136
           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
25137
         }]);
25138
       </script>
25139
       <div ng-controller="ExampleController">
25140
         <input ng-model="title"> <br/>
25141
         <textarea ng-model="text"></textarea> <br/>
25142
         <pane title="{{title}}">{{text}}</pane>
25143
       </div>
25144
     </file>
25145
     <file name="protractor.js" type="protractor">
25146
        it('should have transcluded', function() {
25147
          var titleElement = element(by.model('title'));
25148
          titleElement.clear();
25149
          titleElement.sendKeys('TITLE');
25150
          var textElement = element(by.model('text'));
25151
          textElement.clear();
25152
          textElement.sendKeys('TEXT');
25153
          expect(element(by.binding('title')).getText()).toEqual('TITLE');
25154
          expect(element(by.binding('text')).getText()).toEqual('TEXT');
25155
        });
25156
     </file>
25157
   </example>
25158
 *
25159
 */
25160
var ngTranscludeDirective = ngDirective({
25161
  restrict: 'EAC',
25162
  link: function($scope, $element, $attrs, controller, $transclude) {
25163
    if (!$transclude) {
25164
      throw minErr('ngTransclude')('orphan',
25165
       'Illegal use of ngTransclude directive in the template! ' +
25166
       'No parent directive that requires a transclusion found. ' +
25167
       'Element: {0}',
25168
       startingTag($element));
25169
    }
25170
 
25171
    $transclude(function(clone) {
25172
      $element.empty();
25173
      $element.append(clone);
25174
    });
25175
  }
25176
});
25177
 
25178
/**
25179
 * @ngdoc directive
25180
 * @name script
25181
 * @restrict E
25182
 *
25183
 * @description
25184
 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
25185
 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
25186
 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
25187
 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
25188
 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
25189
 *
25190
 * @param {string} type Must be set to `'text/ng-template'`.
25191
 * @param {string} id Cache name of the template.
25192
 *
25193
 * @example
25194
  <example>
25195
    <file name="index.html">
25196
      <script type="text/ng-template" id="/tpl.html">
25197
        Content of the template.
25198
      </script>
25199
 
25200
      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
25201
      <div id="tpl-content" ng-include src="currentTpl"></div>
25202
    </file>
25203
    <file name="protractor.js" type="protractor">
25204
      it('should load template defined inside script tag', function() {
25205
        element(by.css('#tpl-link')).click();
25206
        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
25207
      });
25208
    </file>
25209
  </example>
25210
 */
25211
var scriptDirective = ['$templateCache', function($templateCache) {
25212
  return {
25213
    restrict: 'E',
25214
    terminal: true,
25215
    compile: function(element, attr) {
25216
      if (attr.type == 'text/ng-template') {
25217
        var templateUrl = attr.id,
25218
            text = element[0].text;
25219
 
25220
        $templateCache.put(templateUrl, text);
25221
      }
25222
    }
25223
  };
25224
}];
25225
 
25226
var ngOptionsMinErr = minErr('ngOptions');
25227
/**
25228
 * @ngdoc directive
25229
 * @name select
25230
 * @restrict E
25231
 *
25232
 * @description
25233
 * HTML `SELECT` element with angular data-binding.
25234
 *
25235
 * # `ngOptions`
25236
 *
25237
 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25238
 * elements for the `<select>` element using the array or object obtained by evaluating the
25239
 * `ngOptions` comprehension_expression.
25240
 *
25241
 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25242
 * similar result. However, the `ngOptions` provides some benefits such as reducing memory and
25243
 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25244
 * more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be
25245
 * used when the `select` model needs to be bound to a non-string value. This is because an option
25246
 * element can only be bound to string values at present.
25247
 *
25248
 * When an item in the `<select>` menu is selected, the array element or object property
25249
 * represented by the selected option will be bound to the model identified by the `ngModel`
25250
 * directive.
25251
 *
25252
 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25253
 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25254
 * option. See example below for demonstration.
25255
 *
25256
 * <div class="alert alert-warning">
25257
 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
25258
 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
25259
 * </div>
25260
 *
25261
 * ## `select as`
25262
 *
25263
 * Using `select as` will bind the result of the `select as` expression to the model, but
25264
 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25265
 * or property name (for object data sources) of the value within the collection. If a `track by` expression
25266
 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25267
 *
25268
 * ### `select as` with `track by`
25269
 *
25270
 * Using `select as` together with `track by` is not recommended. Reasoning:
25271
 *
25272
 * - Example: &lt;select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"&gt;
25273
 *   values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}],
25274
 *   $scope.selected = {name: 'aSubItem'};
25275
 * - track by is always applied to `value`, with the purpose of preserving the selection,
25276
 *   (to `item` in this case)
25277
 * - to calculate whether an item is selected we do the following:
25278
 *   1. apply `track by` to the values in the array, e.g.
25279
 *      In the example: [1,2]
25280
 *   2. apply `track by` to the already selected value in `ngModel`:
25281
 *      In the example: this is not possible, as `track by` refers to `item.id`, but the selected
25282
 *      value from `ngModel` is `{name: aSubItem}`.
25283
 *
25284
 * @param {string} ngModel Assignable angular expression to data-bind to.
25285
 * @param {string=} name Property name of the form under which the control is published.
25286
 * @param {string=} required The control is considered valid only if value is entered.
25287
 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25288
 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25289
 *    `required` when you want to data-bind to the `required` attribute.
25290
 * @param {comprehension_expression=} ngOptions in one of the following forms:
25291
 *
25292
 *   * for array data sources:
25293
 *     * `label` **`for`** `value` **`in`** `array`
25294
 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25295
 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25296
 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25297
 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
25298
 *        (for including a filter with `track by`)
25299
 *   * for object data sources:
25300
 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25301
 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25302
 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25303
 *     * `select` **`as`** `label` **`group by`** `group`
25304
 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25305
 *
25306
 * Where:
25307
 *
25308
 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25309
 *   * `value`: local variable which will refer to each item in the `array` or each property value
25310
 *      of `object` during iteration.
25311
 *   * `key`: local variable which will refer to a property name in `object` during iteration.
25312
 *   * `label`: The result of this expression will be the label for `<option>` element. The
25313
 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25314
 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
25315
 *      element. If not specified, `select` expression will default to `value`.
25316
 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
25317
 *      DOM element.
25318
 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25319
 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
25320
 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25321
 *      even when the options are recreated (e.g. reloaded from the server).
25322
 *
25323
 * @example
25324
    <example module="selectExample">
25325
      <file name="index.html">
25326
        <script>
25327
        angular.module('selectExample', [])
25328
          .controller('ExampleController', ['$scope', function($scope) {
25329
            $scope.colors = [
25330
              {name:'black', shade:'dark'},
25331
              {name:'white', shade:'light'},
25332
              {name:'red', shade:'dark'},
25333
              {name:'blue', shade:'dark'},
25334
              {name:'yellow', shade:'light'}
25335
            ];
25336
            $scope.myColor = $scope.colors[2]; // red
25337
          }]);
25338
        </script>
25339
        <div ng-controller="ExampleController">
25340
          <ul>
25341
            <li ng-repeat="color in colors">
25342
              Name: <input ng-model="color.name">
25343
              [<a href ng-click="colors.splice($index, 1)">X</a>]
25344
            </li>
25345
            <li>
25346
              [<a href ng-click="colors.push({})">add</a>]
25347
            </li>
25348
          </ul>
25349
          <hr/>
25350
          Color (null not allowed):
25351
          <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
25352
 
25353
          Color (null allowed):
25354
          <span  class="nullable">
25355
            <select ng-model="myColor" ng-options="color.name for color in colors">
25356
              <option value="">-- choose color --</option>
25357
            </select>
25358
          </span><br/>
25359
 
25360
          Color grouped by shade:
25361
          <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25362
          </select><br/>
25363
 
25364
 
25365
          Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
25366
          <hr/>
25367
          Currently selected: {{ {selected_color:myColor} }}
25368
          <div style="border:solid 1px black; height:20px"
25369
               ng-style="{'background-color':myColor.name}">
25370
          </div>
25371
        </div>
25372
      </file>
25373
      <file name="protractor.js" type="protractor">
25374
         it('should check ng-options', function() {
25375
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25376
           element.all(by.model('myColor')).first().click();
25377
           element.all(by.css('select[ng-model="myColor"] option')).first().click();
25378
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25379
           element(by.css('.nullable select[ng-model="myColor"]')).click();
25380
           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25381
           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25382
         });
25383
      </file>
25384
    </example>
25385
 */
25386
 
25387
var ngOptionsDirective = valueFn({
25388
  restrict: 'A',
25389
  terminal: true
25390
});
25391
 
25392
// jshint maxlen: false
25393
var selectDirective = ['$compile', '$parse', function($compile,   $parse) {
25394
                         //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
25395
  var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\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]+?))?$/,
25396
      nullModelCtrl = {$setViewValue: noop};
25397
// jshint maxlen: 100
25398
 
25399
  return {
25400
    restrict: 'E',
25401
    require: ['select', '?ngModel'],
25402
    controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
25403
      var self = this,
25404
          optionsMap = {},
25405
          ngModelCtrl = nullModelCtrl,
25406
          nullOption,
25407
          unknownOption;
25408
 
25409
 
25410
      self.databound = $attrs.ngModel;
25411
 
25412
 
25413
      self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
25414
        ngModelCtrl = ngModelCtrl_;
25415
        nullOption = nullOption_;
25416
        unknownOption = unknownOption_;
25417
      };
25418
 
25419
 
25420
      self.addOption = function(value, element) {
25421
        assertNotHasOwnProperty(value, '"option value"');
25422
        optionsMap[value] = true;
25423
 
25424
        if (ngModelCtrl.$viewValue == value) {
25425
          $element.val(value);
25426
          if (unknownOption.parent()) unknownOption.remove();
25427
        }
25428
        // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
25429
        // Adding an <option selected="selected"> element to a <select required="required"> should
25430
        // automatically select the new element
25431
        if (element && element[0].hasAttribute('selected')) {
25432
          element[0].selected = true;
25433
        }
25434
      };
25435
 
25436
 
25437
      self.removeOption = function(value) {
25438
        if (this.hasOption(value)) {
25439
          delete optionsMap[value];
25440
          if (ngModelCtrl.$viewValue === value) {
25441
            this.renderUnknownOption(value);
25442
          }
25443
        }
25444
      };
25445
 
25446
 
25447
      self.renderUnknownOption = function(val) {
25448
        var unknownVal = '? ' + hashKey(val) + ' ?';
25449
        unknownOption.val(unknownVal);
25450
        $element.prepend(unknownOption);
25451
        $element.val(unknownVal);
25452
        unknownOption.prop('selected', true); // needed for IE
25453
      };
25454
 
25455
 
25456
      self.hasOption = function(value) {
25457
        return optionsMap.hasOwnProperty(value);
25458
      };
25459
 
25460
      $scope.$on('$destroy', function() {
25461
        // disable unknown option so that we don't do work when the whole select is being destroyed
25462
        self.renderUnknownOption = noop;
25463
      });
25464
    }],
25465
 
25466
    link: function(scope, element, attr, ctrls) {
25467
      // if ngModel is not defined, we don't need to do anything
25468
      if (!ctrls[1]) return;
25469
 
25470
      var selectCtrl = ctrls[0],
25471
          ngModelCtrl = ctrls[1],
25472
          multiple = attr.multiple,
25473
          optionsExp = attr.ngOptions,
25474
          nullOption = false, // if false, user will not be able to select it (used by ngOptions)
25475
          emptyOption,
25476
          renderScheduled = false,
25477
          // we can't just jqLite('<option>') since jqLite is not smart enough
25478
          // to create it in <select> and IE barfs otherwise.
25479
          optionTemplate = jqLite(document.createElement('option')),
25480
          optGroupTemplate =jqLite(document.createElement('optgroup')),
25481
          unknownOption = optionTemplate.clone();
25482
 
25483
      // find "null" option
25484
      for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
25485
        if (children[i].value === '') {
25486
          emptyOption = nullOption = children.eq(i);
25487
          break;
25488
        }
25489
      }
25490
 
25491
      selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
25492
 
25493
      // required validator
25494
      if (multiple) {
25495
        ngModelCtrl.$isEmpty = function(value) {
25496
          return !value || value.length === 0;
25497
        };
25498
      }
25499
 
25500
      if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
25501
      else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
25502
      else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
25503
 
25504
 
25505
      ////////////////////////////
25506
 
25507
 
25508
 
25509
      function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
25510
        ngModelCtrl.$render = function() {
25511
          var viewValue = ngModelCtrl.$viewValue;
25512
 
25513
          if (selectCtrl.hasOption(viewValue)) {
25514
            if (unknownOption.parent()) unknownOption.remove();
25515
            selectElement.val(viewValue);
25516
            if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
25517
          } else {
25518
            if (isUndefined(viewValue) && emptyOption) {
25519
              selectElement.val('');
25520
            } else {
25521
              selectCtrl.renderUnknownOption(viewValue);
25522
            }
25523
          }
25524
        };
25525
 
25526
        selectElement.on('change', function() {
25527
          scope.$apply(function() {
25528
            if (unknownOption.parent()) unknownOption.remove();
25529
            ngModelCtrl.$setViewValue(selectElement.val());
25530
          });
25531
        });
25532
      }
25533
 
25534
      function setupAsMultiple(scope, selectElement, ctrl) {
25535
        var lastView;
25536
        ctrl.$render = function() {
25537
          var items = new HashMap(ctrl.$viewValue);
25538
          forEach(selectElement.find('option'), function(option) {
25539
            option.selected = isDefined(items.get(option.value));
25540
          });
25541
        };
25542
 
25543
        // we have to do it on each watch since ngModel watches reference, but
25544
        // we need to work of an array, so we need to see if anything was inserted/removed
25545
        scope.$watch(function selectMultipleWatch() {
25546
          if (!equals(lastView, ctrl.$viewValue)) {
25547
            lastView = shallowCopy(ctrl.$viewValue);
25548
            ctrl.$render();
25549
          }
25550
        });
25551
 
25552
        selectElement.on('change', function() {
25553
          scope.$apply(function() {
25554
            var array = [];
25555
            forEach(selectElement.find('option'), function(option) {
25556
              if (option.selected) {
25557
                array.push(option.value);
25558
              }
25559
            });
25560
            ctrl.$setViewValue(array);
25561
          });
25562
        });
25563
      }
25564
 
25565
      function setupAsOptions(scope, selectElement, ctrl) {
25566
        var match;
25567
 
25568
        if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25569
          throw ngOptionsMinErr('iexp',
25570
            "Expected expression in form of " +
25571
            "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25572
            " but got '{0}'. Element: {1}",
25573
            optionsExp, startingTag(selectElement));
25574
        }
25575
 
25576
        var displayFn = $parse(match[2] || match[1]),
25577
            valueName = match[4] || match[6],
25578
            selectAs = / as /.test(match[0]) && match[1],
25579
            selectAsFn = selectAs ? $parse(selectAs) : null,
25580
            keyName = match[5],
25581
            groupByFn = $parse(match[3] || ''),
25582
            valueFn = $parse(match[2] ? match[1] : valueName),
25583
            valuesFn = $parse(match[7]),
25584
            track = match[8],
25585
            trackFn = track ? $parse(match[8]) : null,
25586
            trackKeysCache = {},
25587
            // This is an array of array of existing option groups in DOM.
25588
            // We try to reuse these if possible
25589
            // - optionGroupsCache[0] is the options with no option group
25590
            // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
25591
            optionGroupsCache = [[{element: selectElement, label:''}]],
25592
            //re-usable object to represent option's locals
25593
            locals = {};
25594
 
25595
        if (nullOption) {
25596
          // compile the element since there might be bindings in it
25597
          $compile(nullOption)(scope);
25598
 
25599
          // remove the class, which is added automatically because we recompile the element and it
25600
          // becomes the compilation root
25601
          nullOption.removeClass('ng-scope');
25602
 
25603
          // we need to remove it before calling selectElement.empty() because otherwise IE will
25604
          // remove the label from the element. wtf?
25605
          nullOption.remove();
25606
        }
25607
 
25608
        // clear contents, we'll add what's needed based on the model
25609
        selectElement.empty();
25610
 
25611
        selectElement.on('change', selectionChanged);
25612
 
25613
        ctrl.$render = render;
25614
 
25615
        scope.$watchCollection(valuesFn, scheduleRendering);
25616
        scope.$watchCollection(getLabels, scheduleRendering);
25617
 
25618
        if (multiple) {
25619
          scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
25620
        }
25621
 
25622
        // ------------------------------------------------------------------ //
25623
 
25624
        function callExpression(exprFn, key, value) {
25625
          locals[valueName] = value;
25626
          if (keyName) locals[keyName] = key;
25627
          return exprFn(scope, locals);
25628
        }
25629
 
25630
        function selectionChanged() {
25631
          scope.$apply(function() {
25632
            var collection = valuesFn(scope) || [];
25633
            var viewValue;
25634
            if (multiple) {
25635
              viewValue = [];
25636
              forEach(selectElement.val(), function(selectedKey) {
25637
                  selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;
25638
                viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
25639
              });
25640
            } else {
25641
              var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();
25642
              viewValue = getViewValue(selectedKey, collection[selectedKey]);
25643
            }
25644
            ctrl.$setViewValue(viewValue);
25645
            render();
25646
          });
25647
        }
25648
 
25649
        function getViewValue(key, value) {
25650
          if (key === '?') {
25651
            return undefined;
25652
          } else if (key === '') {
25653
            return null;
25654
          } else {
25655
            var viewValueFn = selectAsFn ? selectAsFn : valueFn;
25656
            return callExpression(viewValueFn, key, value);
25657
          }
25658
        }
25659
 
25660
        function getLabels() {
25661
          var values = valuesFn(scope);
25662
          var toDisplay;
25663
          if (values && isArray(values)) {
25664
            toDisplay = new Array(values.length);
25665
            for (var i = 0, ii = values.length; i < ii; i++) {
25666
              toDisplay[i] = callExpression(displayFn, i, values[i]);
25667
            }
25668
            return toDisplay;
25669
          } else if (values) {
25670
            // TODO: Add a test for this case
25671
            toDisplay = {};
25672
            for (var prop in values) {
25673
              if (values.hasOwnProperty(prop)) {
25674
                toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
25675
              }
25676
            }
25677
          }
25678
          return toDisplay;
25679
        }
25680
 
25681
        function createIsSelectedFn(viewValue) {
25682
          var selectedSet;
25683
          if (multiple) {
25684
            if (trackFn && isArray(viewValue)) {
25685
 
25686
              selectedSet = new HashMap([]);
25687
              for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
25688
                // tracking by key
25689
                selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
25690
              }
25691
            } else {
25692
              selectedSet = new HashMap(viewValue);
25693
            }
25694
          } else if (trackFn) {
25695
            viewValue = callExpression(trackFn, null, viewValue);
25696
          }
25697
 
25698
          return function isSelected(key, value) {
25699
            var compareValueFn;
25700
            if (trackFn) {
25701
              compareValueFn = trackFn;
25702
            } else if (selectAsFn) {
25703
              compareValueFn = selectAsFn;
25704
            } else {
25705
              compareValueFn = valueFn;
25706
            }
25707
 
25708
            if (multiple) {
25709
              return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
25710
            } else {
25711
              return viewValue === callExpression(compareValueFn, key, value);
25712
            }
25713
          };
25714
        }
25715
 
25716
        function scheduleRendering() {
25717
          if (!renderScheduled) {
25718
            scope.$$postDigest(render);
25719
            renderScheduled = true;
25720
          }
25721
        }
25722
 
25723
        /**
25724
         * A new labelMap is created with each render.
25725
         * This function is called for each existing option with added=false,
25726
         * and each new option with added=true.
25727
         * - Labels that are passed to this method twice,
25728
         * (once with added=true and once with added=false) will end up with a value of 0, and
25729
         * will cause no change to happen to the corresponding option.
25730
         * - Labels that are passed to this method only once with added=false will end up with a
25731
         * value of -1 and will eventually be passed to selectCtrl.removeOption()
25732
         * - Labels that are passed to this method only once with added=true will end up with a
25733
         * value of 1 and will eventually be passed to selectCtrl.addOption()
25734
        */
25735
        function updateLabelMap(labelMap, label, added) {
25736
          labelMap[label] = labelMap[label] || 0;
25737
          labelMap[label] += (added ? 1 : -1);
25738
        }
25739
 
25740
        function render() {
25741
          renderScheduled = false;
25742
 
25743
          // Temporary location for the option groups before we render them
25744
          var optionGroups = {'':[]},
25745
              optionGroupNames = [''],
25746
              optionGroupName,
25747
              optionGroup,
25748
              option,
25749
              existingParent, existingOptions, existingOption,
25750
              viewValue = ctrl.$viewValue,
25751
              values = valuesFn(scope) || [],
25752
              keys = keyName ? sortedKeys(values) : values,
25753
              key,
25754
              value,
25755
              groupLength, length,
25756
              groupIndex, index,
25757
              labelMap = {},
25758
              selected,
25759
              isSelected = createIsSelectedFn(viewValue),
25760
              anySelected = false,
25761
              lastElement,
25762
              element,
25763
              label,
25764
              optionId;
25765
 
25766
          trackKeysCache = {};
25767
 
25768
          // We now build up the list of options we need (we merge later)
25769
          for (index = 0; length = keys.length, index < length; index++) {
25770
            key = index;
25771
            if (keyName) {
25772
              key = keys[index];
25773
              if (key.charAt(0) === '$') continue;
25774
            }
25775
            value = values[key];
25776
 
25777
            optionGroupName = callExpression(groupByFn, key, value) || '';
25778
            if (!(optionGroup = optionGroups[optionGroupName])) {
25779
              optionGroup = optionGroups[optionGroupName] = [];
25780
              optionGroupNames.push(optionGroupName);
25781
            }
25782
 
25783
            selected = isSelected(key, value);
25784
            anySelected = anySelected || selected;
25785
 
25786
            label = callExpression(displayFn, key, value); // what will be seen by the user
25787
 
25788
            // doing displayFn(scope, locals) || '' overwrites zero values
25789
            label = isDefined(label) ? label : '';
25790
            optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);
25791
            if (trackFn) {
25792
              trackKeysCache[optionId] = key;
25793
            }
25794
 
25795
            optionGroup.push({
25796
              // either the index into array or key from object
25797
              id: optionId,
25798
              label: label,
25799
              selected: selected                   // determine if we should be selected
25800
            });
25801
          }
25802
          if (!multiple) {
25803
            if (nullOption || viewValue === null) {
25804
              // insert null option if we have a placeholder, or the model is null
25805
              optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
25806
            } else if (!anySelected) {
25807
              // option could not be found, we have to insert the undefined item
25808
              optionGroups[''].unshift({id:'?', label:'', selected:true});
25809
            }
25810
          }
25811
 
25812
          // Now we need to update the list of DOM nodes to match the optionGroups we computed above
25813
          for (groupIndex = 0, groupLength = optionGroupNames.length;
25814
               groupIndex < groupLength;
25815
               groupIndex++) {
25816
            // current option group name or '' if no group
25817
            optionGroupName = optionGroupNames[groupIndex];
25818
 
25819
            // list of options for that group. (first item has the parent)
25820
            optionGroup = optionGroups[optionGroupName];
25821
 
25822
            if (optionGroupsCache.length <= groupIndex) {
25823
              // we need to grow the optionGroups
25824
              existingParent = {
25825
                element: optGroupTemplate.clone().attr('label', optionGroupName),
25826
                label: optionGroup.label
25827
              };
25828
              existingOptions = [existingParent];
25829
              optionGroupsCache.push(existingOptions);
25830
              selectElement.append(existingParent.element);
25831
            } else {
25832
              existingOptions = optionGroupsCache[groupIndex];
25833
              existingParent = existingOptions[0];  // either SELECT (no group) or OPTGROUP element
25834
 
25835
              // update the OPTGROUP label if not the same.
25836
              if (existingParent.label != optionGroupName) {
25837
                existingParent.element.attr('label', existingParent.label = optionGroupName);
25838
              }
25839
            }
25840
 
25841
            lastElement = null;  // start at the beginning
25842
            for (index = 0, length = optionGroup.length; index < length; index++) {
25843
              option = optionGroup[index];
25844
              if ((existingOption = existingOptions[index + 1])) {
25845
                // reuse elements
25846
                lastElement = existingOption.element;
25847
                if (existingOption.label !== option.label) {
25848
                  updateLabelMap(labelMap, existingOption.label, false);
25849
                  updateLabelMap(labelMap, option.label, true);
25850
                  lastElement.text(existingOption.label = option.label);
25851
                  lastElement.prop('label', existingOption.label);
25852
                }
25853
                if (existingOption.id !== option.id) {
25854
                  lastElement.val(existingOption.id = option.id);
25855
                }
25856
                // lastElement.prop('selected') provided by jQuery has side-effects
25857
                if (lastElement[0].selected !== option.selected) {
25858
                  lastElement.prop('selected', (existingOption.selected = option.selected));
25859
                  if (msie) {
25860
                    // See #7692
25861
                    // The selected item wouldn't visually update on IE without this.
25862
                    // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
25863
                    lastElement.prop('selected', existingOption.selected);
25864
                  }
25865
                }
25866
              } else {
25867
                // grow elements
25868
 
25869
                // if it's a null option
25870
                if (option.id === '' && nullOption) {
25871
                  // put back the pre-compiled element
25872
                  element = nullOption;
25873
                } else {
25874
                  // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
25875
                  // in this version of jQuery on some browser the .text() returns a string
25876
                  // rather then the element.
25877
                  (element = optionTemplate.clone())
25878
                      .val(option.id)
25879
                      .prop('selected', option.selected)
25880
                      .attr('selected', option.selected)
25881
                      .prop('label', option.label)
25882
                      .text(option.label);
25883
                }
25884
 
25885
                existingOptions.push(existingOption = {
25886
                    element: element,
25887
                    label: option.label,
25888
                    id: option.id,
25889
                    selected: option.selected
25890
                });
25891
                updateLabelMap(labelMap, option.label, true);
25892
                if (lastElement) {
25893
                  lastElement.after(element);
25894
                } else {
25895
                  existingParent.element.append(element);
25896
                }
25897
                lastElement = element;
25898
              }
25899
            }
25900
            // remove any excessive OPTIONs in a group
25901
            index++; // increment since the existingOptions[0] is parent element not OPTION
25902
            while (existingOptions.length > index) {
25903
              option = existingOptions.pop();
25904
              updateLabelMap(labelMap, option.label, false);
25905
              option.element.remove();
25906
            }
25907
          }
25908
          // remove any excessive OPTGROUPs from select
25909
          while (optionGroupsCache.length > groupIndex) {
25910
            // remove all the labels in the option group
25911
            optionGroup = optionGroupsCache.pop();
25912
            for (index = 1; index < optionGroup.length; ++index) {
25913
              updateLabelMap(labelMap, optionGroup[index].label, false);
25914
            }
25915
            optionGroup[0].element.remove();
25916
          }
25917
          forEach(labelMap, function(count, label) {
25918
            if (count > 0) {
25919
              selectCtrl.addOption(label);
25920
            } else if (count < 0) {
25921
              selectCtrl.removeOption(label);
25922
            }
25923
          });
25924
        }
25925
      }
25926
    }
25927
  };
25928
}];
25929
 
25930
var optionDirective = ['$interpolate', function($interpolate) {
25931
  var nullSelectCtrl = {
25932
    addOption: noop,
25933
    removeOption: noop
25934
  };
25935
 
25936
  return {
25937
    restrict: 'E',
25938
    priority: 100,
25939
    compile: function(element, attr) {
25940
      if (isUndefined(attr.value)) {
25941
        var interpolateFn = $interpolate(element.text(), true);
25942
        if (!interpolateFn) {
25943
          attr.$set('value', element.text());
25944
        }
25945
      }
25946
 
25947
      return function(scope, element, attr) {
25948
        var selectCtrlName = '$selectController',
25949
            parent = element.parent(),
25950
            selectCtrl = parent.data(selectCtrlName) ||
25951
              parent.parent().data(selectCtrlName); // in case we are in optgroup
25952
 
25953
        if (!selectCtrl || !selectCtrl.databound) {
25954
          selectCtrl = nullSelectCtrl;
25955
        }
25956
 
25957
        if (interpolateFn) {
25958
          scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
25959
            attr.$set('value', newVal);
25960
            if (oldVal !== newVal) {
25961
              selectCtrl.removeOption(oldVal);
25962
            }
25963
            selectCtrl.addOption(newVal, element);
25964
          });
25965
        } else {
25966
          selectCtrl.addOption(attr.value, element);
25967
        }
25968
 
25969
        element.on('$destroy', function() {
25970
          selectCtrl.removeOption(attr.value);
25971
        });
25972
      };
25973
    }
25974
  };
25975
}];
25976
 
25977
var styleDirective = valueFn({
25978
  restrict: 'E',
25979
  terminal: false
25980
});
25981
 
25982
  if (window.angular.bootstrap) {
25983
    //AngularJS is already loaded, so we can return here...
25984
    console.log('WARNING: Tried to load angular more than once.');
25985
    return;
25986
  }
25987
 
25988
  //try to bind to jquery now so that one can write jqLite(document).ready()
25989
  //but we will rebind on bootstrap again.
25990
  bindJQuery();
25991
 
25992
  publishExternalAPI(angular);
25993
 
25994
  jqLite(document).ready(function() {
25995
    angularInit(document, bootstrap);
25996
  });
25997
 
25998
})(window, document);
25999
 
26000
!window.angular.$$csp() && window.angular.element(document).find('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;}</style>');