GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (3063)

angularjs/angular-1.6.5/angular-messages.js (2 issues)

Labels
Severity
1
/**
2
 * @license AngularJS v1.6.5
3
 * (c) 2010-2017 Google, Inc. http://angularjs.org
4
 * License: MIT
5
 */
6 View Code Duplication
(function(window, angular) {'use strict';
7
8
var forEach;
9
var isArray;
10
var isString;
11
var jqLite;
12
13
/**
14
 * @ngdoc module
15
 * @name ngMessages
16
 * @description
17
 *
18
 * The `ngMessages` module provides enhanced support for displaying messages within templates
19
 * (typically within forms or when rendering message objects that return key/value data).
20
 * Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
21
 * show and hide error messages specific to the state of an input field, the `ngMessages` and
22
 * `ngMessage` directives are designed to handle the complexity, inheritance and priority
23
 * sequencing based on the order of how the messages are defined in the template.
24
 *
25
 * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
26
 * `ngMessage` and `ngMessageExp` directives.
27
 *
28
 * # Usage
29
 * The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
30
 * (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use
31
 * case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the
32
 * {@link ngModel ngModel} directive.
33
 *
34
 * The child elements of the `ngMessages` directive are matched to the collection keys by a `ngMessage` or
35
 * `ngMessageExp` directive. The value of these attributes must match a key in the collection that is provided by
36
 * the `ngMessages` directive.
37
 *
38
 * Consider the following example, which illustrates a typical use case of `ngMessages`. Within the form `myForm` we
39
 * have a text input named `myField` which is bound to the scope variable `field` using the {@link ngModel ngModel}
40
 * directive.
41
 *
42
 * The `myField` field is a required input of type `email` with a maximum length of 15 characters.
43
 *
44
 * ```html
45
 * <form name="myForm">
46
 *   <label>
47
 *     Enter text:
48
 *     <input type="email" ng-model="field" name="myField" required maxlength="15" />
49
 *   </label>
50
 *   <div ng-messages="myForm.myField.$error" role="alert">
51
 *     <div ng-message="required">Please enter a value for this field.</div>
52
 *     <div ng-message="email">This field must be a valid email address.</div>
53
 *     <div ng-message="maxlength">This field can be at most 15 characters long.</div>
54
 *   </div>
55
 * </form>
56
 * ```
57
 *
58
 * In order to show error messages corresponding to `myField` we first create an element with an `ngMessages` attribute
59
 * set to the `$error` object owned by the `myField` input in our `myForm` form.
60
 *
61
 * Within this element we then create separate elements for each of the possible errors that `myField` could have.
62
 * The `ngMessage` attribute is used to declare which element(s) will appear for which error - for example,
63
 * setting `ng-message="required"` specifies that this particular element should be displayed when there
64
 * is no value present for the required field `myField` (because the key `required` will be `true` in the object
65
 * `myForm.myField.$error`).
66
 *
67
 * ### Message order
68
 *
69
 * By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more
70
 * than one message (or error) key is currently true, then which message is shown is determined by the order of messages
71
 * in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have
72
 * to prioritize messages using custom JavaScript code.
73
 *
74
 * Given the following error object for our example (which informs us that the field `myField` currently has both the
75
 * `required` and `email` errors):
76
 *
77
 * ```javascript
78
 * <!-- keep in mind that ngModel automatically sets these error flags -->
79
 * myField.$error = { required : true, email: true, maxlength: false };
80
 * ```
81
 * The `required` message will be displayed to the user since it appears before the `email` message in the DOM.
82
 * Once the user types a single character, the `required` message will disappear (since the field now has a value)
83
 * but the `email` message will be visible because it is still applicable.
84
 *
85
 * ### Displaying multiple messages at the same time
86
 *
87
 * While `ngMessages` will by default only display one error element at a time, the `ng-messages-multiple` attribute can
88
 * be applied to the `ngMessages` container element to cause it to display all applicable error messages at once:
89
 *
90
 * ```html
91
 * <!-- attribute-style usage -->
92
 * <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
93
 *
94
 * <!-- element-style usage -->
95
 * <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
96
 * ```
97
 *
98
 * ## Reusing and Overriding Messages
99
 * In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
100
 * template. This allows for generic collection of messages to be reused across multiple parts of an
101
 * application.
102
 *
103
 * ```html
104
 * <script type="text/ng-template" id="error-messages">
105
 *   <div ng-message="required">This field is required</div>
106
 *   <div ng-message="minlength">This field is too short</div>
107
 * </script>
108
 *
109
 * <div ng-messages="myForm.myField.$error" role="alert">
110
 *   <div ng-messages-include="error-messages"></div>
111
 * </div>
112
 * ```
113
 *
114
 * However, including generic messages may not be useful enough to match all input fields, therefore,
115
 * `ngMessages` provides the ability to override messages defined in the remote template by redefining
116
 * them within the directive container.
117
 *
118
 * ```html
119
 * <!-- a generic template of error messages known as "my-custom-messages" -->
120
 * <script type="text/ng-template" id="my-custom-messages">
121
 *   <div ng-message="required">This field is required</div>
122
 *   <div ng-message="minlength">This field is too short</div>
123
 * </script>
124
 *
125
 * <form name="myForm">
126
 *   <label>
127
 *     Email address
128
 *     <input type="email"
129
 *            id="email"
130
 *            name="myEmail"
131
 *            ng-model="email"
132
 *            minlength="5"
133
 *            required />
134
 *   </label>
135
 *   <!-- any ng-message elements that appear BEFORE the ng-messages-include will
136
 *        override the messages present in the ng-messages-include template -->
137
 *   <div ng-messages="myForm.myEmail.$error" role="alert">
138
 *     <!-- this required message has overridden the template message -->
139
 *     <div ng-message="required">You did not enter your email address</div>
140
 *
141
 *     <!-- this is a brand new message and will appear last in the prioritization -->
142
 *     <div ng-message="email">Your email address is invalid</div>
143
 *
144
 *     <!-- and here are the generic error messages -->
145
 *     <div ng-messages-include="my-custom-messages"></div>
146
 *   </div>
147
 * </form>
148
 * ```
149
 *
150
 * In the example HTML code above the message that is set on required will override the corresponding
151
 * required message defined within the remote template. Therefore, with particular input fields (such
152
 * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
153
 * while more generic messages can be used to handle other, more general input errors.
154
 *
155
 * ## Dynamic Messaging
156
 * ngMessages also supports using expressions to dynamically change key values. Using arrays and
157
 * repeaters to list messages is also supported. This means that the code below will be able to
158
 * fully adapt itself and display the appropriate message when any of the expression data changes:
159
 *
160
 * ```html
161
 * <form name="myForm">
162
 *   <label>
163
 *     Email address
164
 *     <input type="email"
165
 *            name="myEmail"
166
 *            ng-model="email"
167
 *            minlength="5"
168
 *            required />
169
 *   </label>
170
 *   <div ng-messages="myForm.myEmail.$error" role="alert">
171
 *     <div ng-message="required">You did not enter your email address</div>
172
 *     <div ng-repeat="errorMessage in errorMessages">
173
 *       <!-- use ng-message-exp for a message whose key is given by an expression -->
174
 *       <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
175
 *     </div>
176
 *   </div>
177
 * </form>
178
 * ```
179
 *
180
 * The `errorMessage.type` expression can be a string value or it can be an array so
181
 * that multiple errors can be associated with a single error message:
182
 *
183
 * ```html
184
 *   <label>
185
 *     Email address
186
 *     <input type="email"
187
 *            ng-model="data.email"
188
 *            name="myEmail"
189
 *            ng-minlength="5"
190
 *            ng-maxlength="100"
191
 *            required />
192
 *   </label>
193
 *   <div ng-messages="myForm.myEmail.$error" role="alert">
194
 *     <div ng-message-exp="'required'">You did not enter your email address</div>
195
 *     <div ng-message-exp="['minlength', 'maxlength']">
196
 *       Your email must be between 5 and 100 characters long
197
 *     </div>
198
 *   </div>
199
 * ```
200
 *
201
 * Feel free to use other structural directives such as ng-if and ng-switch to further control
202
 * what messages are active and when. Be careful, if you place ng-message on the same element
203
 * as these structural directives, Angular may not be able to determine if a message is active
204
 * or not. Therefore it is best to place the ng-message on a child element of the structural
205
 * directive.
206
 *
207
 * ```html
208
 * <div ng-messages="myForm.myEmail.$error" role="alert">
209
 *   <div ng-if="showRequiredError">
210
 *     <div ng-message="required">Please enter something</div>
211
 *   </div>
212
 * </div>
213
 * ```
214
 *
215
 * ## Animations
216
 * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
217
 * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
218
 * the DOM by the `ngMessages` directive.
219
 *
220
 * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
221
 * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
222
 * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
223
 * hook into the animations whenever these classes are added/removed.
224
 *
225
 * Let's say that our HTML code for our messages container looks like so:
226
 *
227
 * ```html
228
 * <div ng-messages="myMessages" class="my-messages" role="alert">
229
 *   <div ng-message="alert" class="some-message">...</div>
230
 *   <div ng-message="fail" class="some-message">...</div>
231
 * </div>
232
 * ```
233
 *
234
 * Then the CSS animation code for the message container looks like so:
235
 *
236
 * ```css
237
 * .my-messages {
238
 *   transition:1s linear all;
239
 * }
240
 * .my-messages.ng-active {
241
 *   // messages are visible
242
 * }
243
 * .my-messages.ng-inactive {
244
 *   // messages are hidden
245
 * }
246
 * ```
247
 *
248
 * Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
249
 * and leave animation is triggered for each particular element bound to the `ngMessage` directive.
250
 *
251
 * Therefore, the CSS code for the inner messages looks like so:
252
 *
253
 * ```css
254
 * .some-message {
255
 *   transition:1s linear all;
256
 * }
257
 *
258
 * .some-message.ng-enter {}
259
 * .some-message.ng-enter.ng-enter-active {}
260
 *
261
 * .some-message.ng-leave {}
262
 * .some-message.ng-leave.ng-leave-active {}
263
 * ```
264
 *
265
 * {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
266
 */
267
angular.module('ngMessages', [], function initAngularHelpers() {
268
  // Access helpers from angular core.
269
  // Do it inside a `config` block to ensure `window.angular` is available.
270
  forEach = angular.forEach;
271
  isArray = angular.isArray;
272
  isString = angular.isString;
273
  jqLite = angular.element;
274
})
275
  .info({ angularVersion: '1.6.5' })
276
277
  /**
278
   * @ngdoc directive
279
   * @module ngMessages
280
   * @name ngMessages
281
   * @restrict AE
282
   *
283
   * @description
284
   * `ngMessages` is a directive that is designed to show and hide messages based on the state
285
   * of a key/value object that it listens on. The directive itself complements error message
286
   * reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
287
   *
288
   * `ngMessages` manages the state of internal messages within its container element. The internal
289
   * messages use the `ngMessage` directive and will be inserted/removed from the page depending
290
   * on if they're present within the key/value object. By default, only one message will be displayed
291
   * at a time and this depends on the prioritization of the messages within the template. (This can
292
   * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
293
   *
294
   * A remote template can also be used to promote message reusability and messages can also be
295
   * overridden.
296
   *
297
   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
298
   *
299
   * @usage
300
   * ```html
301
   * <!-- using attribute directives -->
302
   * <ANY ng-messages="expression" role="alert">
303
   *   <ANY ng-message="stringValue">...</ANY>
304
   *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
305
   *   <ANY ng-message-exp="expressionValue">...</ANY>
306
   * </ANY>
307
   *
308
   * <!-- or by using element directives -->
309
   * <ng-messages for="expression" role="alert">
310
   *   <ng-message when="stringValue">...</ng-message>
311
   *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
312
   *   <ng-message when-exp="expressionValue">...</ng-message>
313
   * </ng-messages>
314
   * ```
315
   *
316
   * @param {string} ngMessages an angular expression evaluating to a key/value object
317
   *                 (this is typically the $error object on an ngModel instance).
318
   * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
319
   *
320
   * @example
321
   * <example name="ngMessages-directive" module="ngMessagesExample"
322
   *          deps="angular-messages.js"
323
   *          animations="true" fixBase="true">
324
   *   <file name="index.html">
325
   *     <form name="myForm">
326
   *       <label>
327
   *         Enter your name:
328
   *         <input type="text"
329
   *                name="myName"
330
   *                ng-model="name"
331
   *                ng-minlength="5"
332
   *                ng-maxlength="20"
333
   *                required />
334
   *       </label>
335
   *       <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
336
   *
337
   *       <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
338
   *         <div ng-message="required">You did not enter a field</div>
339
   *         <div ng-message="minlength">Your field is too short</div>
340
   *         <div ng-message="maxlength">Your field is too long</div>
341
   *       </div>
342
   *     </form>
343
   *   </file>
344
   *   <file name="script.js">
345
   *     angular.module('ngMessagesExample', ['ngMessages']);
346
   *   </file>
347
   * </example>
348
   */
349
  .directive('ngMessages', ['$animate', function($animate) {
350
    var ACTIVE_CLASS = 'ng-active';
351
    var INACTIVE_CLASS = 'ng-inactive';
352
353
    return {
354
      require: 'ngMessages',
355
      restrict: 'AE',
356
      controller: ['$element', '$scope', '$attrs', function NgMessagesCtrl($element, $scope, $attrs) {
357
        var ctrl = this;
358
        var latestKey = 0;
359
        var nextAttachId = 0;
360
361
        this.getAttachId = function getAttachId() { return nextAttachId++; };
362
363
        var messages = this.messages = {};
364
        var renderLater, cachedCollection;
365
366
        this.render = function(collection) {
367
          collection = collection || {};
368
369
          renderLater = false;
370
          cachedCollection = collection;
371
372
          // this is true if the attribute is empty or if the attribute value is truthy
373
          var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
374
                         isAttrTruthy($scope, $attrs.multiple);
375
376
          var unmatchedMessages = [];
377
          var matchedKeys = {};
378
          var messageItem = ctrl.head;
379
          var messageFound = false;
380
          var totalMessages = 0;
381
382
          // we use != instead of !== to allow for both undefined and null values
383
          while (messageItem != null) {
384
            totalMessages++;
385
            var messageCtrl = messageItem.message;
386
387
            var messageUsed = false;
388
            if (!messageFound) {
389
              forEach(collection, function(value, key) {
390
                if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
0 ignored issues
show
The variable messageUsed is changed as part of the while loop for example by false on line 387. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
The variable messageCtrl is changed as part of the while loop for example by messageItem.message on line 385. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
391
                  // this is to prevent the same error name from showing up twice
392
                  if (matchedKeys[key]) return;
393
                  matchedKeys[key] = true;
394
395
                  messageUsed = true;
396
                  messageCtrl.attach();
397
                }
398
              });
399
            }
400
401
            if (messageUsed) {
402
              // unless we want to display multiple messages then we should
403
              // set a flag here to avoid displaying the next message in the list
404
              messageFound = !multiple;
405
            } else {
406
              unmatchedMessages.push(messageCtrl);
407
            }
408
409
            messageItem = messageItem.next;
410
          }
411
412
          forEach(unmatchedMessages, function(messageCtrl) {
413
            messageCtrl.detach();
414
          });
415
416
          if (unmatchedMessages.length !== totalMessages) {
417
            $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
418
          } else {
419
            $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
420
          }
421
        };
422
423
        $scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
424
425
        // If the element is destroyed, proactively destroy all the currently visible messages
426
        $element.on('$destroy', function() {
427
          forEach(messages, function(item) {
428
            item.message.detach();
429
          });
430
        });
431
432
        this.reRender = function() {
433
          if (!renderLater) {
434
            renderLater = true;
435
            $scope.$evalAsync(function() {
436
              if (renderLater && cachedCollection) {
437
                ctrl.render(cachedCollection);
438
              }
439
            });
440
          }
441
        };
442
443
        this.register = function(comment, messageCtrl) {
444
          var nextKey = latestKey.toString();
445
          messages[nextKey] = {
446
            message: messageCtrl
447
          };
448
          insertMessageNode($element[0], comment, nextKey);
449
          comment.$$ngMessageNode = nextKey;
450
          latestKey++;
451
452
          ctrl.reRender();
453
        };
454
455
        this.deregister = function(comment) {
456
          var key = comment.$$ngMessageNode;
457
          delete comment.$$ngMessageNode;
458
          removeMessageNode($element[0], comment, key);
459
          delete messages[key];
460
          ctrl.reRender();
461
        };
462
463
        function findPreviousMessage(parent, comment) {
464
          var prevNode = comment;
465
          var parentLookup = [];
466
467
          while (prevNode && prevNode !== parent) {
468
            var prevKey = prevNode.$$ngMessageNode;
469
            if (prevKey && prevKey.length) {
470
              return messages[prevKey];
471
            }
472
473
            // dive deeper into the DOM and examine its children for any ngMessage
474
            // comments that may be in an element that appears deeper in the list
475
            if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) {
476
              parentLookup.push(prevNode);
477
              prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
478
            } else if (prevNode.previousSibling) {
479
              prevNode = prevNode.previousSibling;
480
            } else {
481
              prevNode = prevNode.parentNode;
482
              parentLookup.push(prevNode);
483
            }
484
          }
485
        }
486
487
        function insertMessageNode(parent, comment, key) {
488
          var messageNode = messages[key];
489
          if (!ctrl.head) {
490
            ctrl.head = messageNode;
491
          } else {
492
            var match = findPreviousMessage(parent, comment);
493
            if (match) {
494
              messageNode.next = match.next;
495
              match.next = messageNode;
496
            } else {
497
              messageNode.next = ctrl.head;
498
              ctrl.head = messageNode;
499
            }
500
          }
501
        }
502
503
        function removeMessageNode(parent, comment, key) {
504
          var messageNode = messages[key];
505
506
          var match = findPreviousMessage(parent, comment);
507
          if (match) {
508
            match.next = messageNode.next;
509
          } else {
510
            ctrl.head = messageNode.next;
511
          }
512
        }
513
      }]
514
    };
515
516
    function isAttrTruthy(scope, attr) {
517
     return (isString(attr) && attr.length === 0) || //empty attribute
518
            truthy(scope.$eval(attr));
519
    }
520
521
    function truthy(val) {
522
      return isString(val) ? val.length : !!val;
523
    }
524
  }])
525
526
  /**
527
   * @ngdoc directive
528
   * @name ngMessagesInclude
529
   * @restrict AE
530
   * @scope
531
   *
532
   * @description
533
   * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
534
   * code from a remote template and place the downloaded template code into the exact spot
535
   * that the ngMessagesInclude directive is placed within the ngMessages container. This allows
536
   * for a series of pre-defined messages to be reused and also allows for the developer to
537
   * determine what messages are overridden due to the placement of the ngMessagesInclude directive.
538
   *
539
   * @usage
540
   * ```html
541
   * <!-- using attribute directives -->
542
   * <ANY ng-messages="expression" role="alert">
543
   *   <ANY ng-messages-include="remoteTplString">...</ANY>
544
   * </ANY>
545
   *
546
   * <!-- or by using element directives -->
547
   * <ng-messages for="expression" role="alert">
548
   *   <ng-messages-include src="expressionValue1">...</ng-messages-include>
549
   * </ng-messages>
550
   * ```
551
   *
552
   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
553
   *
554
   * @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
555
   */
556
  .directive('ngMessagesInclude',
557
    ['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
558
559
    return {
560
      restrict: 'AE',
561
      require: '^^ngMessages', // we only require this for validation sake
562
      link: function($scope, element, attrs) {
563
        var src = attrs.ngMessagesInclude || attrs.src;
564
        $templateRequest(src).then(function(html) {
565
          if ($scope.$$destroyed) return;
566
567
          if (isString(html) && !html.trim()) {
568
            // Empty template - nothing to compile
569
            replaceElementWithMarker(element, src);
570
          } else {
571
            // Non-empty template - compile and link
572
            $compile(html)($scope, function(contents) {
573
              element.after(contents);
574
              replaceElementWithMarker(element, src);
575
            });
576
          }
577
        });
578
      }
579
    };
580
581
    // Helpers
582
    function replaceElementWithMarker(element, src) {
583
      // A comment marker is placed for debugging purposes
584
      var comment = $compile.$$createComment ?
585
          $compile.$$createComment('ngMessagesInclude', src) :
586
          $document[0].createComment(' ngMessagesInclude: ' + src + ' ');
587
      var marker = jqLite(comment);
588
      element.after(marker);
589
590
      // Don't pollute the DOM anymore by keeping an empty directive element
591
      element.remove();
592
    }
593
  }])
594
595
  /**
596
   * @ngdoc directive
597
   * @name ngMessage
598
   * @restrict AE
599
   * @scope
600
   *
601
   * @description
602
   * `ngMessage` is a directive with the purpose to show and hide a particular message.
603
   * For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
604
   * must be situated since it determines which messages are visible based on the state
605
   * of the provided key/value map that `ngMessages` listens on.
606
   *
607
   * More information about using `ngMessage` can be found in the
608
   * {@link module:ngMessages `ngMessages` module documentation}.
609
   *
610
   * @usage
611
   * ```html
612
   * <!-- using attribute directives -->
613
   * <ANY ng-messages="expression" role="alert">
614
   *   <ANY ng-message="stringValue">...</ANY>
615
   *   <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
616
   * </ANY>
617
   *
618
   * <!-- or by using element directives -->
619
   * <ng-messages for="expression" role="alert">
620
   *   <ng-message when="stringValue">...</ng-message>
621
   *   <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
622
   * </ng-messages>
623
   * ```
624
   *
625
   * @param {expression} ngMessage|when a string value corresponding to the message key.
626
   */
627
  .directive('ngMessage', ngMessageDirectiveFactory())
628
629
630
  /**
631
   * @ngdoc directive
632
   * @name ngMessageExp
633
   * @restrict AE
634
   * @priority 1
635
   * @scope
636
   *
637
   * @description
638
   * `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
639
   * value, it accepts an expression to be evaluated for the message key.
640
   *
641
   * @usage
642
   * ```html
643
   * <!-- using attribute directives -->
644
   * <ANY ng-messages="expression">
645
   *   <ANY ng-message-exp="expressionValue">...</ANY>
646
   * </ANY>
647
   *
648
   * <!-- or by using element directives -->
649
   * <ng-messages for="expression">
650
   *   <ng-message when-exp="expressionValue">...</ng-message>
651
   * </ng-messages>
652
   * ```
653
   *
654
   * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
655
   *
656
   * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
657
   */
658
  .directive('ngMessageExp', ngMessageDirectiveFactory());
659
660
function ngMessageDirectiveFactory() {
661
  return ['$animate', function($animate) {
662
    return {
663
      restrict: 'AE',
664
      transclude: 'element',
665
      priority: 1, // must run before ngBind, otherwise the text is set on the comment
666
      terminal: true,
667
      require: '^^ngMessages',
668
      link: function(scope, element, attrs, ngMessagesCtrl, $transclude) {
669
        var commentNode = element[0];
670
671
        var records;
672
        var staticExp = attrs.ngMessage || attrs.when;
673
        var dynamicExp = attrs.ngMessageExp || attrs.whenExp;
674
        var assignRecords = function(items) {
675
          records = items
676
              ? (isArray(items)
677
                  ? items
678
                  : items.split(/[\s,]+/))
679
              : null;
680
          ngMessagesCtrl.reRender();
681
        };
682
683
        if (dynamicExp) {
684
          assignRecords(scope.$eval(dynamicExp));
685
          scope.$watchCollection(dynamicExp, assignRecords);
686
        } else {
687
          assignRecords(staticExp);
688
        }
689
690
        var currentElement, messageCtrl;
691
        ngMessagesCtrl.register(commentNode, messageCtrl = {
692
          test: function(name) {
693
            return contains(records, name);
694
          },
695
          attach: function() {
696
            if (!currentElement) {
697
              $transclude(function(elm, newScope) {
698
                $animate.enter(elm, null, element);
699
                currentElement = elm;
700
701
                // Each time we attach this node to a message we get a new id that we can match
702
                // when we are destroying the node later.
703
                var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
704
705
                // in the event that the element or a parent element is destroyed
706
                // by another structural directive then it's time
707
                // to deregister the message from the controller
708
                currentElement.on('$destroy', function() {
709
                  if (currentElement && currentElement.$$attachId === $$attachId) {
710
                    ngMessagesCtrl.deregister(commentNode);
711
                    messageCtrl.detach();
712
                  }
713
                  newScope.$destroy();
714
                });
715
              });
716
            }
717
          },
718
          detach: function() {
719
            if (currentElement) {
720
              var elm = currentElement;
721
              currentElement = null;
722
              $animate.leave(elm);
723
            }
724
          }
725
        });
726
      }
727
    };
728
  }];
729
730
  function contains(collection, key) {
731
    if (collection) {
732
      return isArray(collection)
733
          ? collection.indexOf(key) >= 0
734
          : collection.hasOwnProperty(key);
735
    }
736
  }
737
}
738
739
740
})(window, window.angular);
741