angularjs-rails 1.3.15 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/angularjs-rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/angular-animate.js +3443 -1872
  4. data/vendor/assets/javascripts/angular-aria.js +89 -75
  5. data/vendor/assets/javascripts/angular-cookies.js +255 -141
  6. data/vendor/assets/javascripts/angular-loader.js +41 -17
  7. data/vendor/assets/javascripts/angular-message-format.js +980 -0
  8. data/vendor/assets/javascripts/angular-messages.js +430 -153
  9. data/vendor/assets/javascripts/angular-mocks.js +76 -92
  10. data/vendor/assets/javascripts/angular-resource.js +6 -6
  11. data/vendor/assets/javascripts/angular-route.js +8 -6
  12. data/vendor/assets/javascripts/angular-sanitize.js +32 -28
  13. data/vendor/assets/javascripts/angular-scenario.js +4315 -2452
  14. data/vendor/assets/javascripts/angular-touch.js +26 -21
  15. data/vendor/assets/javascripts/angular.js +4314 -2490
  16. data/vendor/assets/javascripts/unstable/angular2.js +24024 -0
  17. metadata +4 -14
  18. data/vendor/assets/javascripts/unstable/angular-animate.js +0 -2137
  19. data/vendor/assets/javascripts/unstable/angular-aria.js +0 -364
  20. data/vendor/assets/javascripts/unstable/angular-cookies.js +0 -206
  21. data/vendor/assets/javascripts/unstable/angular-loader.js +0 -405
  22. data/vendor/assets/javascripts/unstable/angular-messages.js +0 -401
  23. data/vendor/assets/javascripts/unstable/angular-mocks.js +0 -2468
  24. data/vendor/assets/javascripts/unstable/angular-resource.js +0 -668
  25. data/vendor/assets/javascripts/unstable/angular-route.js +0 -989
  26. data/vendor/assets/javascripts/unstable/angular-sanitize.js +0 -679
  27. data/vendor/assets/javascripts/unstable/angular-scenario.js +0 -37678
  28. data/vendor/assets/javascripts/unstable/angular-touch.js +0 -622
  29. data/vendor/assets/javascripts/unstable/angular.js +0 -26309
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @license AngularJS v1.3.15
3
- * (c) 2010-2014 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.4.0
3
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
6
 
@@ -39,28 +39,33 @@
39
39
  function minErr(module, ErrorConstructor) {
40
40
  ErrorConstructor = ErrorConstructor || Error;
41
41
  return function() {
42
- var code = arguments[0],
43
- prefix = '[' + (module ? module + ':' : '') + code + '] ',
44
- template = arguments[1],
45
- templateArgs = arguments,
42
+ var SKIP_INDEXES = 2;
46
43
 
47
- message, i;
44
+ var templateArgs = arguments,
45
+ code = templateArgs[0],
46
+ message = '[' + (module ? module + ':' : '') + code + '] ',
47
+ template = templateArgs[1],
48
+ paramPrefix, i;
48
49
 
49
- message = prefix + template.replace(/\{\d+\}/g, function(match) {
50
- var index = +match.slice(1, -1), arg;
50
+ message += template.replace(/\{\d+\}/g, function(match) {
51
+ var index = +match.slice(1, -1),
52
+ shiftedIndex = index + SKIP_INDEXES;
51
53
 
52
- if (index + 2 < templateArgs.length) {
53
- return toDebugString(templateArgs[index + 2]);
54
+ if (shiftedIndex < templateArgs.length) {
55
+ return toDebugString(templateArgs[shiftedIndex]);
54
56
  }
57
+
55
58
  return match;
56
59
  });
57
60
 
58
- message = message + '\nhttp://errors.angularjs.org/1.3.15/' +
61
+ message += '\nhttp://errors.angularjs.org/1.4.0/' +
59
62
  (module ? module + '/' : '') + code;
60
- for (i = 2; i < arguments.length; i++) {
61
- message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
62
- encodeURIComponent(toDebugString(arguments[i]));
63
+
64
+ for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
65
+ message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
66
+ encodeURIComponent(toDebugString(templateArgs[i]));
63
67
  }
68
+
64
69
  return new ErrorConstructor(message);
65
70
  };
66
71
  }
@@ -258,6 +263,18 @@ function setupModuleLoader(window) {
258
263
  */
259
264
  constant: invokeLater('$provide', 'constant', 'unshift'),
260
265
 
266
+ /**
267
+ * @ngdoc method
268
+ * @name angular.Module#decorator
269
+ * @module ng
270
+ * @param {string} The name of the service to decorate.
271
+ * @param {Function} This function will be invoked when the service needs to be
272
+ * instantiated and should return the decorated service instance.
273
+ * @description
274
+ * See {@link auto.$provide#decorator $provide.decorator()}.
275
+ */
276
+ decorator: invokeLater('$provide', 'decorator'),
277
+
261
278
  /**
262
279
  * @ngdoc method
263
280
  * @name angular.Module#animation
@@ -271,7 +288,7 @@ function setupModuleLoader(window) {
271
288
  *
272
289
  *
273
290
  * Defines an animation hook that can be later used with
274
- * {@link ngAnimate.$animate $animate} service and directives that use this service.
291
+ * {@link $animate $animate} service and directives that use this service.
275
292
  *
276
293
  * ```js
277
294
  * module.animation('.animation-name', function($inject1, $inject2) {
@@ -296,10 +313,17 @@ function setupModuleLoader(window) {
296
313
  * @ngdoc method
297
314
  * @name angular.Module#filter
298
315
  * @module ng
299
- * @param {string} name Filter name.
316
+ * @param {string} name Filter name - this must be a valid angular expression identifier
300
317
  * @param {Function} filterFactory Factory function for creating new instance of filter.
301
318
  * @description
302
319
  * See {@link ng.$filterProvider#register $filterProvider.register()}.
320
+ *
321
+ * <div class="alert alert-warning">
322
+ * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
323
+ * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
324
+ * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
325
+ * (`myapp_subsection_filterx`).
326
+ * </div>
303
327
  */
304
328
  filter: invokeLater('$filterProvider', 'register'),
305
329
 
@@ -0,0 +1,980 @@
1
+ /**
2
+ * @license AngularJS v1.4.0
3
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ // NOTE: ADVANCED_OPTIMIZATIONS mode.
9
+ //
10
+ // This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
11
+ // constructs incompatible with that mode.
12
+
13
+ var $interpolateMinErr = window['angular']['$interpolateMinErr'];
14
+
15
+ var noop = window['angular']['noop'],
16
+ isFunction = window['angular']['isFunction'],
17
+ toJson = window['angular']['toJson'];
18
+
19
+ function stringify(value) {
20
+ if (value == null /* null/undefined */) { return ''; }
21
+ switch (typeof value) {
22
+ case 'string': return value;
23
+ case 'number': return '' + value;
24
+ default: return toJson(value);
25
+ }
26
+ }
27
+
28
+ // Convert an index into the string into line/column for use in error messages
29
+ // As such, this doesn't have to be efficient.
30
+ function indexToLineAndColumn(text, index) {
31
+ var lines = text.split(/\n/g);
32
+ for (var i=0; i < lines.length; i++) {
33
+ var line=lines[i];
34
+ if (index >= line.length) {
35
+ index -= line.length;
36
+ } else {
37
+ return { line: i + 1, column: index + 1 };
38
+ }
39
+ }
40
+ }
41
+ var PARSE_CACHE_FOR_TEXT_LITERALS = Object.create(null);
42
+
43
+ function parseTextLiteral(text) {
44
+ var cachedFn = PARSE_CACHE_FOR_TEXT_LITERALS[text];
45
+ if (cachedFn != null) {
46
+ return cachedFn;
47
+ }
48
+ function parsedFn(context) { return text; }
49
+ parsedFn['$$watchDelegate'] = function watchDelegate(scope, listener, objectEquality) {
50
+ var unwatch = scope['$watch'](noop,
51
+ function textLiteralWatcher() {
52
+ if (isFunction(listener)) { listener.call(null, text, text, scope); }
53
+ unwatch();
54
+ },
55
+ objectEquality);
56
+ return unwatch;
57
+ };
58
+ PARSE_CACHE_FOR_TEXT_LITERALS[text] = parsedFn;
59
+ parsedFn['exp'] = text; // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
60
+ parsedFn['expressions'] = []; // Require this to call $compile.$$addBindingInfo() which allows Protractor to find elements by binding.
61
+ return parsedFn;
62
+ }
63
+
64
+ function subtractOffset(expressionFn, offset) {
65
+ if (offset === 0) {
66
+ return expressionFn;
67
+ }
68
+ function minusOffset(value) {
69
+ return (value == void 0) ? value : value - offset;
70
+ }
71
+ function parsedFn(context) { return minusOffset(expressionFn(context)); }
72
+ var unwatch;
73
+ parsedFn['$$watchDelegate'] = function watchDelegate(scope, listener, objectEquality) {
74
+ unwatch = scope['$watch'](expressionFn,
75
+ function pluralExpressionWatchListener(newValue, oldValue) {
76
+ if (isFunction(listener)) { listener.call(null, minusOffset(newValue), minusOffset(oldValue), scope); }
77
+ },
78
+ objectEquality);
79
+ return unwatch;
80
+ };
81
+ return parsedFn;
82
+ }
83
+
84
+ // NOTE: ADVANCED_OPTIMIZATIONS mode.
85
+ //
86
+ // This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
87
+ // constructs incompatible with that mode.
88
+
89
+ /* global $interpolateMinErr: false */
90
+ /* global isFunction: false */
91
+ /* global noop: false */
92
+
93
+ /**
94
+ * @constructor
95
+ * @private
96
+ */
97
+ function MessageSelectorBase(expressionFn, choices) {
98
+ var self = this;
99
+ this.expressionFn = expressionFn;
100
+ this.choices = choices;
101
+ if (choices["other"] === void 0) {
102
+ throw $interpolateMinErr('reqother', '“other” is a required option.');
103
+ }
104
+ this.parsedFn = function(context) { return self.getResult(context); };
105
+ this.parsedFn['$$watchDelegate'] = function $$watchDelegate(scope, listener, objectEquality) {
106
+ return self.watchDelegate(scope, listener, objectEquality);
107
+ };
108
+ this.parsedFn['exp'] = expressionFn['exp'];
109
+ this.parsedFn['expressions'] = expressionFn['expressions'];
110
+ }
111
+
112
+ MessageSelectorBase.prototype.getMessageFn = function getMessageFn(value) {
113
+ return this.choices[this.categorizeValue(value)];
114
+ };
115
+
116
+ MessageSelectorBase.prototype.getResult = function getResult(context) {
117
+ return this.getMessageFn(this.expressionFn(context))(context);
118
+ };
119
+
120
+ MessageSelectorBase.prototype.watchDelegate = function watchDelegate(scope, listener, objectEquality) {
121
+ var watchers = new MessageSelectorWatchers(this, scope, listener, objectEquality);
122
+ return function() { watchers.cancelWatch(); };
123
+ };
124
+
125
+ /**
126
+ * @constructor
127
+ * @private
128
+ */
129
+ function MessageSelectorWatchers(msgSelector, scope, listener, objectEquality) {
130
+ var self = this;
131
+ this.scope = scope;
132
+ this.msgSelector = msgSelector;
133
+ this.listener = listener;
134
+ this.objectEquality = objectEquality;
135
+ this.lastMessage = void 0;
136
+ this.messageFnWatcher = noop;
137
+ var expressionFnListener = function(newValue, oldValue) { return self.expressionFnListener(newValue, oldValue); };
138
+ this.expressionFnWatcher = scope['$watch'](msgSelector.expressionFn, expressionFnListener, objectEquality);
139
+ }
140
+
141
+ MessageSelectorWatchers.prototype.expressionFnListener = function expressionFnListener(newValue, oldValue) {
142
+ var self = this;
143
+ this.messageFnWatcher();
144
+ var messageFnListener = function(newMessage, oldMessage) { return self.messageFnListener(newMessage, oldMessage); };
145
+ var messageFn = this.msgSelector.getMessageFn(newValue);
146
+ this.messageFnWatcher = this.scope['$watch'](messageFn, messageFnListener, this.objectEquality);
147
+ };
148
+
149
+ MessageSelectorWatchers.prototype.messageFnListener = function messageFnListener(newMessage, oldMessage) {
150
+ if (isFunction(this.listener)) {
151
+ this.listener.call(null, newMessage, newMessage === oldMessage ? newMessage : this.lastMessage, this.scope);
152
+ }
153
+ this.lastMessage = newMessage;
154
+ };
155
+
156
+ MessageSelectorWatchers.prototype.cancelWatch = function cancelWatch() {
157
+ this.expressionFnWatcher();
158
+ this.messageFnWatcher();
159
+ };
160
+
161
+ /**
162
+ * @constructor
163
+ * @extends MessageSelectorBase
164
+ * @private
165
+ */
166
+ function SelectMessage(expressionFn, choices) {
167
+ MessageSelectorBase.call(this, expressionFn, choices);
168
+ }
169
+
170
+ function SelectMessageProto() {}
171
+ SelectMessageProto.prototype = MessageSelectorBase.prototype;
172
+
173
+ SelectMessage.prototype = new SelectMessageProto();
174
+ SelectMessage.prototype.categorizeValue = function categorizeSelectValue(value) {
175
+ return (this.choices[value] !== void 0) ? value : "other";
176
+ };
177
+
178
+ /**
179
+ * @constructor
180
+ * @extends MessageSelectorBase
181
+ * @private
182
+ */
183
+ function PluralMessage(expressionFn, choices, offset, pluralCat) {
184
+ MessageSelectorBase.call(this, expressionFn, choices);
185
+ this.offset = offset;
186
+ this.pluralCat = pluralCat;
187
+ }
188
+
189
+ function PluralMessageProto() {}
190
+ PluralMessageProto.prototype = MessageSelectorBase.prototype;
191
+
192
+ PluralMessage.prototype = new PluralMessageProto();
193
+ PluralMessage.prototype.categorizeValue = function categorizePluralValue(value) {
194
+ if (isNaN(value)) {
195
+ return "other";
196
+ } else if (this.choices[value] !== void 0) {
197
+ return value;
198
+ } else {
199
+ var category = this.pluralCat(value - this.offset);
200
+ return (this.choices[category] !== void 0) ? category : "other";
201
+ }
202
+ };
203
+
204
+ // NOTE: ADVANCED_OPTIMIZATIONS mode.
205
+ //
206
+ // This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
207
+ // constructs incompatible with that mode.
208
+
209
+ /* global $interpolateMinErr: false */
210
+ /* global isFunction: false */
211
+ /* global parseTextLiteral: false */
212
+
213
+ /**
214
+ * @constructor
215
+ * @private
216
+ */
217
+ function InterpolationParts(trustedContext, allOrNothing) {
218
+ this.trustedContext = trustedContext;
219
+ this.allOrNothing = allOrNothing;
220
+ this.textParts = [];
221
+ this.expressionFns = [];
222
+ this.expressionIndices = [];
223
+ this.partialText = '';
224
+ this.concatParts = null;
225
+ }
226
+
227
+ InterpolationParts.prototype.flushPartialText = function flushPartialText() {
228
+ if (this.partialText) {
229
+ if (this.concatParts == null) {
230
+ this.textParts.push(this.partialText);
231
+ } else {
232
+ this.textParts.push(this.concatParts.join(''));
233
+ this.concatParts = null;
234
+ }
235
+ this.partialText = '';
236
+ }
237
+ };
238
+
239
+ InterpolationParts.prototype.addText = function addText(text) {
240
+ if (text.length) {
241
+ if (!this.partialText) {
242
+ this.partialText = text;
243
+ } else if (this.concatParts) {
244
+ this.concatParts.push(text);
245
+ } else {
246
+ this.concatParts = [this.partialText, text];
247
+ }
248
+ }
249
+ };
250
+
251
+ InterpolationParts.prototype.addExpressionFn = function addExpressionFn(expressionFn) {
252
+ this.flushPartialText();
253
+ this.expressionIndices.push(this.textParts.length);
254
+ this.expressionFns.push(expressionFn);
255
+ this.textParts.push('');
256
+ };
257
+
258
+ InterpolationParts.prototype.getExpressionValues = function getExpressionValues(context) {
259
+ var expressionValues = new Array(this.expressionFns.length);
260
+ for (var i = 0; i < this.expressionFns.length; i++) {
261
+ expressionValues[i] = this.expressionFns[i](context);
262
+ }
263
+ return expressionValues;
264
+ };
265
+
266
+ InterpolationParts.prototype.getResult = function getResult(expressionValues) {
267
+ for (var i = 0; i < this.expressionIndices.length; i++) {
268
+ var expressionValue = expressionValues[i];
269
+ if (this.allOrNothing && expressionValue === void 0) return;
270
+ this.textParts[this.expressionIndices[i]] = expressionValue;
271
+ }
272
+ return this.textParts.join('');
273
+ };
274
+
275
+
276
+ InterpolationParts.prototype.toParsedFn = function toParsedFn(mustHaveExpression, originalText) {
277
+ var self = this;
278
+ this.flushPartialText();
279
+ if (mustHaveExpression && this.expressionFns.length === 0) {
280
+ return void 0;
281
+ }
282
+ if (this.textParts.length === 0) {
283
+ return parseTextLiteral('');
284
+ }
285
+ if (this.trustedContext && this.textParts.length > 1) {
286
+ $interpolateMinErr['throwNoconcat'](originalText);
287
+ }
288
+ if (this.expressionFns.length === 0) {
289
+ if (this.textParts.length != 1) { this.errorInParseLogic(); }
290
+ return parseTextLiteral(this.textParts[0]);
291
+ }
292
+ var parsedFn = function(context) {
293
+ return self.getResult(self.getExpressionValues(context));
294
+ };
295
+ parsedFn['$$watchDelegate'] = function $$watchDelegate(scope, listener, objectEquality) {
296
+ return self.watchDelegate(scope, listener, objectEquality);
297
+ };
298
+
299
+ parsedFn['exp'] = originalText; // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
300
+ parsedFn['expressions'] = new Array(this.expressionFns.length); // Require this to call $compile.$$addBindingInfo() which allows Protractor to find elements by binding.
301
+ for (var i = 0; i < this.expressionFns.length; i++) {
302
+ parsedFn['expressions'][i] = this.expressionFns[i]['exp'];
303
+ }
304
+
305
+ return parsedFn;
306
+ };
307
+
308
+ InterpolationParts.prototype.watchDelegate = function watchDelegate(scope, listener, objectEquality) {
309
+ var watcher = new InterpolationPartsWatcher(this, scope, listener, objectEquality);
310
+ return function() { watcher.cancelWatch(); };
311
+ };
312
+
313
+ function InterpolationPartsWatcher(interpolationParts, scope, listener, objectEquality) {
314
+ this.interpolationParts = interpolationParts;
315
+ this.scope = scope;
316
+ this.previousResult = (void 0);
317
+ this.listener = listener;
318
+ var self = this;
319
+ this.expressionFnsWatcher = scope['$watchGroup'](interpolationParts.expressionFns, function(newExpressionValues, oldExpressionValues) {
320
+ self.watchListener(newExpressionValues, oldExpressionValues);
321
+ });
322
+ }
323
+
324
+ InterpolationPartsWatcher.prototype.watchListener = function watchListener(newExpressionValues, oldExpressionValues) {
325
+ var result = this.interpolationParts.getResult(newExpressionValues);
326
+ if (isFunction(this.listener)) {
327
+ this.listener.call(null, result, newExpressionValues === oldExpressionValues ? result : this.previousResult, this.scope);
328
+ }
329
+ this.previousResult = result;
330
+ };
331
+
332
+ InterpolationPartsWatcher.prototype.cancelWatch = function cancelWatch() {
333
+ this.expressionFnsWatcher();
334
+ };
335
+
336
+ // NOTE: ADVANCED_OPTIMIZATIONS mode.
337
+ //
338
+ // This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
339
+ // constructs incompatible with that mode.
340
+
341
+ /* global $interpolateMinErr: false */
342
+ /* global indexToLineAndColumn: false */
343
+ /* global InterpolationParts: false */
344
+ /* global PluralMessage: false */
345
+ /* global SelectMessage: false */
346
+ /* global subtractOffset: false */
347
+
348
+ // The params src and dst are exactly one of two types: NestedParserState or MessageFormatParser.
349
+ // This function is fully optimized by V8. (inspect via IRHydra or --trace-deopt.)
350
+ // The idea behind writing it this way is to avoid repeating oneself. This is the ONE place where
351
+ // the parser state that is saved/restored when parsing nested mustaches is specified.
352
+ function copyNestedParserState(src, dst) {
353
+ dst.expressionFn = src.expressionFn;
354
+ dst.expressionMinusOffsetFn = src.expressionMinusOffsetFn;
355
+ dst.pluralOffset = src.pluralOffset;
356
+ dst.choices = src.choices;
357
+ dst.choiceKey = src.choiceKey;
358
+ dst.interpolationParts = src.interpolationParts;
359
+ dst.ruleChoiceKeyword = src.ruleChoiceKeyword;
360
+ dst.msgStartIndex = src.msgStartIndex;
361
+ dst.expressionStartIndex = src.expressionStartIndex;
362
+ }
363
+
364
+ function NestedParserState(parser) {
365
+ copyNestedParserState(parser, this);
366
+ }
367
+
368
+ /**
369
+ * @constructor
370
+ * @private
371
+ */
372
+ function MessageFormatParser(text, startIndex, $parse, pluralCat, stringifier,
373
+ mustHaveExpression, trustedContext, allOrNothing) {
374
+ this.text = text;
375
+ this.index = startIndex || 0;
376
+ this.$parse = $parse;
377
+ this.pluralCat = pluralCat;
378
+ this.stringifier = stringifier;
379
+ this.mustHaveExpression = !!mustHaveExpression;
380
+ this.trustedContext = trustedContext;
381
+ this.allOrNothing = !!allOrNothing;
382
+ this.expressionFn = null;
383
+ this.expressionMinusOffsetFn = null;
384
+ this.pluralOffset = null;
385
+ this.choices = null;
386
+ this.choiceKey = null;
387
+ this.interpolationParts = null;
388
+ this.msgStartIndex = null;
389
+ this.nestedStateStack = [];
390
+ this.parsedFn = null;
391
+ this.rule = null;
392
+ this.ruleStack = null;
393
+ this.ruleChoiceKeyword = null;
394
+ this.interpNestLevel = null;
395
+ this.expressionStartIndex = null;
396
+ this.stringStartIndex = null;
397
+ this.stringQuote = null;
398
+ this.stringInterestsRe = null;
399
+ this.angularOperatorStack = null;
400
+ this.textPart = null;
401
+ }
402
+
403
+ // preserve v8 optimization.
404
+ var EMPTY_STATE = new NestedParserState(new MessageFormatParser(
405
+ /* text= */ '', /* startIndex= */ 0, /* $parse= */ null, /* pluralCat= */ null, /* stringifier= */ null,
406
+ /* mustHaveExpression= */ false, /* trustedContext= */ null, /* allOrNothing */ false));
407
+
408
+ MessageFormatParser.prototype.pushState = function pushState() {
409
+ this.nestedStateStack.push(new NestedParserState(this));
410
+ copyNestedParserState(EMPTY_STATE, this);
411
+ };
412
+
413
+ MessageFormatParser.prototype.popState = function popState() {
414
+ if (this.nestedStateStack.length === 0) {
415
+ this.errorInParseLogic();
416
+ }
417
+ var previousState = this.nestedStateStack.pop();
418
+ copyNestedParserState(previousState, this);
419
+ };
420
+
421
+ // Oh my JavaScript! Who knew you couldn't match a regex at a specific
422
+ // location in a string but will always search forward?!
423
+ // Apparently you'll be growing this ability via the sticky flag (y) in
424
+ // ES6. I'll just to work around you for now.
425
+ MessageFormatParser.prototype.matchRe = function matchRe(re, search) {
426
+ re.lastIndex = this.index;
427
+ var match = re.exec(this.text);
428
+ if (match != null && (search === true || (match.index == this.index))) {
429
+ this.index = re.lastIndex;
430
+ return match;
431
+ }
432
+ return null;
433
+ };
434
+
435
+ MessageFormatParser.prototype.searchRe = function searchRe(re) {
436
+ return this.matchRe(re, true);
437
+ };
438
+
439
+
440
+ MessageFormatParser.prototype.consumeRe = function consumeRe(re) {
441
+ // Without the sticky flag, we can't use the .test() method to consume a
442
+ // match at the current index. Instead, we'll use the slower .exec() method
443
+ // and verify match.index.
444
+ return !!this.matchRe(re);
445
+ };
446
+
447
+ // Run through our grammar avoiding deeply nested function call chains.
448
+ MessageFormatParser.prototype.run = function run(initialRule) {
449
+ this.ruleStack = [initialRule];
450
+ do {
451
+ this.rule = this.ruleStack.pop();
452
+ while (this.rule) {
453
+ this.rule();
454
+ }
455
+ this.assertRuleOrNull(this.rule);
456
+ } while (this.ruleStack.length > 0);
457
+ };
458
+
459
+ MessageFormatParser.prototype.errorInParseLogic = function errorInParseLogic() {
460
+ throw $interpolateMinErr('logicbug',
461
+ 'The messageformat parser has encountered an internal error. Please file a github issue against the AngularJS project and provide this message text that triggers the bug. Text: “{0}”',
462
+ this.text);
463
+ };
464
+
465
+ MessageFormatParser.prototype.assertRuleOrNull = function assertRuleOrNull(rule) {
466
+ if (rule === void 0) {
467
+ this.errorInParseLogic();
468
+ }
469
+ };
470
+
471
+ var NEXT_WORD_RE = /\s*(\w+)\s*/g;
472
+ MessageFormatParser.prototype.errorExpecting = function errorExpecting() {
473
+ // What was wrong with the syntax? Unsupported type, missing comma, or something else?
474
+ var match = this.matchRe(NEXT_WORD_RE), position;
475
+ if (match == null) {
476
+ position = indexToLineAndColumn(this.text, this.index);
477
+ throw $interpolateMinErr('reqarg',
478
+ 'Expected one of “plural” or “select” at line {0}, column {1} of text “{2}”',
479
+ position.line, position.column, this.text);
480
+ }
481
+ var word = match[1];
482
+ if (word == "select" || word == "plural") {
483
+ position = indexToLineAndColumn(this.text, this.index);
484
+ throw $interpolateMinErr('reqcomma',
485
+ 'Expected a comma after the keyword “{0}” at line {1}, column {2} of text “{3}”',
486
+ word, position.line, position.column, this.text);
487
+ } else {
488
+ position = indexToLineAndColumn(this.text, this.index);
489
+ throw $interpolateMinErr('unknarg',
490
+ 'Unsupported keyword “{0}” at line {0}, column {1}. Only “plural” and “select” are currently supported. Text: “{3}”',
491
+ word, position.line, position.column, this.text);
492
+ }
493
+ };
494
+
495
+ var STRING_START_RE = /['"]/g;
496
+ MessageFormatParser.prototype.ruleString = function ruleString() {
497
+ var match = this.matchRe(STRING_START_RE);
498
+ if (match == null) {
499
+ var position = indexToLineAndColumn(this.text, this.index);
500
+ throw $interpolateMinErr('wantstring',
501
+ 'Expected the beginning of a string at line {0}, column {1} in text “{2}”',
502
+ position.line, position.column, this.text);
503
+ }
504
+ this.startStringAtMatch(match);
505
+ };
506
+
507
+ MessageFormatParser.prototype.startStringAtMatch = function startStringAtMatch(match) {
508
+ this.stringStartIndex = match.index;
509
+ this.stringQuote = match[0];
510
+ this.stringInterestsRe = this.stringQuote == "'" ? SQUOTED_STRING_INTEREST_RE : DQUOTED_STRING_INTEREST_RE;
511
+ this.rule = this.ruleInsideString;
512
+ };
513
+
514
+ var SQUOTED_STRING_INTEREST_RE = /\\(?:\\|'|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{2}|[0-7]{3}|\r\n|\n|[\s\S])|'/g;
515
+ var DQUOTED_STRING_INTEREST_RE = /\\(?:\\|"|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{2}|[0-7]{3}|\r\n|\n|[\s\S])|"/g;
516
+ MessageFormatParser.prototype.ruleInsideString = function ruleInsideString() {
517
+ var match = this.searchRe(this.stringInterestsRe);
518
+ if (match == null) {
519
+ var position = indexToLineAndColumn(this.text, this.stringStartIndex);
520
+ throw $interpolateMinErr('untermstr',
521
+ 'The string beginning at line {0}, column {1} is unterminated in text “{2}”',
522
+ position.line, position.column, this.text);
523
+ }
524
+ var chars = match[0];
525
+ if (match == this.stringQuote) {
526
+ this.rule = null;
527
+ }
528
+ };
529
+
530
+ var PLURAL_OR_SELECT_ARG_TYPE_RE = /\s*(plural|select)\s*,\s*/g;
531
+ MessageFormatParser.prototype.rulePluralOrSelect = function rulePluralOrSelect() {
532
+ var match = this.searchRe(PLURAL_OR_SELECT_ARG_TYPE_RE);
533
+ if (match == null) {
534
+ this.errorExpecting();
535
+ }
536
+ var argType = match[1];
537
+ switch (argType) {
538
+ case "plural": this.rule = this.rulePluralStyle; break;
539
+ case "select": this.rule = this.ruleSelectStyle; break;
540
+ default: this.errorInParseLogic();
541
+ }
542
+ };
543
+
544
+ MessageFormatParser.prototype.rulePluralStyle = function rulePluralStyle() {
545
+ this.choices = Object.create(null);
546
+ this.ruleChoiceKeyword = this.rulePluralValueOrKeyword;
547
+ this.rule = this.rulePluralOffset;
548
+ };
549
+
550
+ MessageFormatParser.prototype.ruleSelectStyle = function ruleSelectStyle() {
551
+ this.choices = Object.create(null);
552
+ this.ruleChoiceKeyword = this.ruleSelectKeyword;
553
+ this.rule = this.ruleSelectKeyword;
554
+ };
555
+
556
+ var NUMBER_RE = /[0]|(?:[1-9][0-9]*)/g;
557
+ var PLURAL_OFFSET_RE = new RegExp("\\s*offset\\s*:\\s*(" + NUMBER_RE.source + ")", "g");
558
+
559
+ MessageFormatParser.prototype.rulePluralOffset = function rulePluralOffset() {
560
+ var match = this.matchRe(PLURAL_OFFSET_RE);
561
+ this.pluralOffset = (match == null) ? 0 : parseInt(match[1], 10);
562
+ this.expressionMinusOffsetFn = subtractOffset(this.expressionFn, this.pluralOffset);
563
+ this.rule = this.rulePluralValueOrKeyword;
564
+ };
565
+
566
+ MessageFormatParser.prototype.assertChoiceKeyIsNew = function assertChoiceKeyIsNew(choiceKey, index) {
567
+ if (this.choices[choiceKey] !== void 0) {
568
+ var position = indexToLineAndColumn(this.text, index);
569
+ throw $interpolateMinErr('dupvalue',
570
+ 'The choice “{0}” is specified more than once. Duplicate key is at line {1}, column {2} in text “{3}”',
571
+ choiceKey, position.line, position.column, this.text);
572
+ }
573
+ };
574
+
575
+ var SELECT_KEYWORD = /\s*(\w+)/g;
576
+ MessageFormatParser.prototype.ruleSelectKeyword = function ruleSelectKeyword() {
577
+ var match = this.matchRe(SELECT_KEYWORD);
578
+ if (match == null) {
579
+ this.parsedFn = new SelectMessage(this.expressionFn, this.choices).parsedFn;
580
+ this.rule = null;
581
+ return;
582
+ }
583
+ this.choiceKey = match[1];
584
+ this.assertChoiceKeyIsNew(this.choiceKey, match.index);
585
+ this.rule = this.ruleMessageText;
586
+ };
587
+
588
+ var EXPLICIT_VALUE_OR_KEYWORD_RE = new RegExp("\\s*(?:(?:=(" + NUMBER_RE.source + "))|(\\w+))", "g");
589
+ MessageFormatParser.prototype.rulePluralValueOrKeyword = function rulePluralValueOrKeyword() {
590
+ var match = this.matchRe(EXPLICIT_VALUE_OR_KEYWORD_RE);
591
+ if (match == null) {
592
+ this.parsedFn = new PluralMessage(this.expressionFn, this.choices, this.pluralOffset, this.pluralCat).parsedFn;
593
+ this.rule = null;
594
+ return;
595
+ }
596
+ if (match[1] != null) {
597
+ this.choiceKey = parseInt(match[1], 10);
598
+ } else {
599
+ this.choiceKey = match[2];
600
+ }
601
+ this.assertChoiceKeyIsNew(this.choiceKey, match.index);
602
+ this.rule = this.ruleMessageText;
603
+ };
604
+
605
+ var BRACE_OPEN_RE = /\s*{/g;
606
+ var BRACE_CLOSE_RE = /}/g;
607
+ MessageFormatParser.prototype.ruleMessageText = function ruleMessageText() {
608
+ if (!this.consumeRe(BRACE_OPEN_RE)) {
609
+ var position = indexToLineAndColumn(this.text, this.index);
610
+ throw $interpolateMinErr('reqopenbrace',
611
+ 'The plural choice “{0}” must be followed by a message in braces at line {1}, column {2} in text “{3}”',
612
+ this.choiceKey, position.line, position.column, this.text);
613
+ }
614
+ this.msgStartIndex = this.index;
615
+ this.interpolationParts = new InterpolationParts(this.trustedContext, this.allOrNothing);
616
+ this.rule = this.ruleInInterpolationOrMessageText;
617
+ };
618
+
619
+ // Note: Since "\" is used as an escape character, don't allow it to be part of the
620
+ // startSymbol/endSymbol when I add the feature to allow them to be redefined.
621
+ var INTERP_OR_END_MESSAGE_RE = /\\.|{{|}/g;
622
+ var INTERP_OR_PLURALVALUE_OR_END_MESSAGE_RE = /\\.|{{|#|}/g;
623
+ var ESCAPE_OR_MUSTACHE_BEGIN_RE = /\\.|{{/g;
624
+ MessageFormatParser.prototype.advanceInInterpolationOrMessageText = function advanceInInterpolationOrMessageText() {
625
+ var currentIndex = this.index, match, re;
626
+ if (this.ruleChoiceKeyword == null) { // interpolation
627
+ match = this.searchRe(ESCAPE_OR_MUSTACHE_BEGIN_RE);
628
+ if (match == null) { // End of interpolation text. Nothing more to process.
629
+ this.textPart = this.text.substring(currentIndex);
630
+ this.index = this.text.length;
631
+ return null;
632
+ }
633
+ } else {
634
+ match = this.searchRe(this.ruleChoiceKeyword == this.rulePluralValueOrKeyword ?
635
+ INTERP_OR_PLURALVALUE_OR_END_MESSAGE_RE : INTERP_OR_END_MESSAGE_RE);
636
+ if (match == null) {
637
+ var position = indexToLineAndColumn(this.text, this.msgStartIndex);
638
+ throw $interpolateMinErr('reqendbrace',
639
+ 'The plural/select choice “{0}” message starting at line {1}, column {2} does not have an ending closing brace. Text “{3}”',
640
+ this.choiceKey, position.line, position.column, this.text);
641
+ }
642
+ }
643
+ // match is non-null.
644
+ var token = match[0];
645
+ this.textPart = this.text.substring(currentIndex, match.index);
646
+ return token;
647
+ };
648
+
649
+ MessageFormatParser.prototype.ruleInInterpolationOrMessageText = function ruleInInterpolationOrMessageText() {
650
+ var currentIndex = this.index;
651
+ var token = this.advanceInInterpolationOrMessageText();
652
+ if (token == null) {
653
+ // End of interpolation text. Nothing more to process.
654
+ this.index = this.text.length;
655
+ this.interpolationParts.addText(this.text.substring(currentIndex));
656
+ this.rule = null;
657
+ return;
658
+ }
659
+ if (token[0] == "\\") {
660
+ // unescape next character and continue
661
+ this.interpolationParts.addText(this.textPart + token[1]);
662
+ return;
663
+ }
664
+ this.interpolationParts.addText(this.textPart);
665
+ if (token == "{{") {
666
+ this.pushState();
667
+ this.ruleStack.push(this.ruleEndMustacheInInterpolationOrMessage);
668
+ this.rule = this.ruleEnteredMustache;
669
+ } else if (token == "}") {
670
+ this.choices[this.choiceKey] = this.interpolationParts.toParsedFn(/*mustHaveExpression=*/false, this.text);
671
+ this.rule = this.ruleChoiceKeyword;
672
+ } else if (token == "#") {
673
+ this.interpolationParts.addExpressionFn(this.expressionMinusOffsetFn);
674
+ } else {
675
+ this.errorInParseLogic();
676
+ }
677
+ };
678
+
679
+ MessageFormatParser.prototype.ruleInterpolate = function ruleInterpolate() {
680
+ this.interpolationParts = new InterpolationParts(this.trustedContext, this.allOrNothing);
681
+ this.rule = this.ruleInInterpolation;
682
+ };
683
+
684
+ MessageFormatParser.prototype.ruleInInterpolation = function ruleInInterpolation() {
685
+ var currentIndex = this.index;
686
+ var match = this.searchRe(ESCAPE_OR_MUSTACHE_BEGIN_RE);
687
+ if (match == null) {
688
+ // End of interpolation text. Nothing more to process.
689
+ this.index = this.text.length;
690
+ this.interpolationParts.addText(this.text.substring(currentIndex));
691
+ this.parsedFn = this.interpolationParts.toParsedFn(this.mustHaveExpression, this.text);
692
+ this.rule = null;
693
+ return;
694
+ }
695
+ var token = match[0];
696
+ if (token[0] == "\\") {
697
+ // unescape next character and continue
698
+ this.interpolationParts.addText(this.text.substring(currentIndex, match.index) + token[1]);
699
+ return;
700
+ }
701
+ this.interpolationParts.addText(this.text.substring(currentIndex, match.index));
702
+ this.pushState();
703
+ this.ruleStack.push(this.ruleInterpolationEndMustache);
704
+ this.rule = this.ruleEnteredMustache;
705
+ };
706
+
707
+ MessageFormatParser.prototype.ruleInterpolationEndMustache = function ruleInterpolationEndMustache() {
708
+ var expressionFn = this.parsedFn;
709
+ this.popState();
710
+ this.interpolationParts.addExpressionFn(expressionFn);
711
+ this.rule = this.ruleInInterpolation;
712
+ };
713
+
714
+ MessageFormatParser.prototype.ruleEnteredMustache = function ruleEnteredMustache() {
715
+ this.parsedFn = null;
716
+ this.ruleStack.push(this.ruleEndMustache);
717
+ this.rule = this.ruleAngularExpression;
718
+ };
719
+
720
+ MessageFormatParser.prototype.ruleEndMustacheInInterpolationOrMessage = function ruleEndMustacheInInterpolationOrMessage() {
721
+ var expressionFn = this.parsedFn;
722
+ this.popState();
723
+ this.interpolationParts.addExpressionFn(expressionFn);
724
+ this.rule = this.ruleInInterpolationOrMessageText;
725
+ };
726
+
727
+
728
+
729
+ var INTERP_END_RE = /\s*}}/g;
730
+ MessageFormatParser.prototype.ruleEndMustache = function ruleEndMustache() {
731
+ var match = this.matchRe(INTERP_END_RE);
732
+ if (match == null) {
733
+ var position = indexToLineAndColumn(this.text, this.index);
734
+ throw $interpolateMinErr('reqendinterp',
735
+ 'Expecting end of interpolation symbol, “{0}”, at line {1}, column {2} in text “{3}”',
736
+ '}}', position.line, position.column, this.text);
737
+ }
738
+ if (this.parsedFn == null) {
739
+ // If we parsed a MessageFormat extension, (e.g. select/plural today, maybe more some other
740
+ // day), then the result *has* to be a string and those rules would have already set
741
+ // this.parsedFn. If there was no MessageFormat extension, then there is no requirement to
742
+ // stringify the result and parsedFn isn't set. We set it here. While we could have set it
743
+ // unconditionally when exiting the Angular expression, I intend for us to not just replace
744
+ // $interpolate, but also to replace $parse in a future version (so ng-bind can work), and in
745
+ // such a case we do not want to unnecessarily stringify something if it's not going to be used
746
+ // in a string context.
747
+ this.parsedFn = this.$parse(this.expressionFn, this.stringifier);
748
+ this.parsedFn['exp'] = this.expressionFn['exp']; // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
749
+ this.parsedFn['expressions'] = this.expressionFn['expressions']; // Require this to call $compile.$$addBindingInfo() which allows Protractor to find elements by binding.
750
+ }
751
+ this.rule = null;
752
+ };
753
+
754
+ MessageFormatParser.prototype.ruleAngularExpression = function ruleAngularExpression() {
755
+ this.angularOperatorStack = [];
756
+ this.expressionStartIndex = this.index;
757
+ this.rule = this.ruleInAngularExpression;
758
+ };
759
+
760
+ function getEndOperator(opBegin) {
761
+ switch (opBegin) {
762
+ case "{": return "}";
763
+ case "[": return "]";
764
+ case "(": return ")";
765
+ default: return null;
766
+ }
767
+ }
768
+
769
+ function getBeginOperator(opEnd) {
770
+ switch (opEnd) {
771
+ case "}": return "{";
772
+ case "]": return "[";
773
+ case ")": return "(";
774
+ default: return null;
775
+ }
776
+ }
777
+
778
+ // TODO(chirayu): The interpolation endSymbol must also be accounted for. It
779
+ // just so happens that "}" is an operator so it's in the list below. But we
780
+ // should support any other type of start/end interpolation symbol.
781
+ var INTERESTING_OPERATORS_RE = /[[\]{}()'",]/g;
782
+ MessageFormatParser.prototype.ruleInAngularExpression = function ruleInAngularExpression() {
783
+ var startIndex = this.index;
784
+ var match = this.searchRe(INTERESTING_OPERATORS_RE);
785
+ var position;
786
+ if (match == null) {
787
+ if (this.angularOperatorStack.length === 0) {
788
+ // This is the end of the Angular expression so this is actually a
789
+ // success. Note that when inside an interpolation, this means we even
790
+ // consumed the closing interpolation symbols if they were curlies. This
791
+ // is NOT an error at this point but will become an error further up the
792
+ // stack when the part that saw the opening curlies is unable to find the
793
+ // closing ones.
794
+ this.index = this.text.length;
795
+ this.expressionFn = this.$parse(this.text.substring(this.expressionStartIndex, this.index));
796
+ // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
797
+ this.expressionFn['exp'] = this.text.substring(this.expressionStartIndex, this.index);
798
+ this.expressionFn['expressions'] = this.expressionFn['expressions'];
799
+ this.rule = null;
800
+ return;
801
+ }
802
+ var innermostOperator = this.angularOperatorStack[0];
803
+ throw $interpolateMinErr('badexpr',
804
+ 'Unexpected end of Angular expression. Expecting operator “{0}” at the end of the text “{1}”',
805
+ this.getEndOperator(innermostOperator), this.text);
806
+ }
807
+ var operator = match[0];
808
+ if (operator == "'" || operator == '"') {
809
+ this.ruleStack.push(this.ruleInAngularExpression);
810
+ this.startStringAtMatch(match);
811
+ return;
812
+ }
813
+ if (operator == ",") {
814
+ if (this.trustedContext) {
815
+ position = indexToLineAndColumn(this.text, this.index);
816
+ throw $interpolateMinErr('unsafe',
817
+ 'Use of select/plural MessageFormat syntax is currently disallowed in a secure context ({0}). At line {1}, column {2} of text “{3}”',
818
+ this.trustedContext, position.line, position.column, this.text);
819
+ }
820
+ // only the top level comma has relevance.
821
+ if (this.angularOperatorStack.length === 0) {
822
+ // todo: does this need to be trimmed?
823
+ this.expressionFn = this.$parse(this.text.substring(this.expressionStartIndex, match.index));
824
+ // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
825
+ this.expressionFn['exp'] = this.text.substring(this.expressionStartIndex, match.index);
826
+ this.expressionFn['expressions'] = this.expressionFn['expressions'];
827
+ this.rule = null;
828
+ this.rule = this.rulePluralOrSelect;
829
+ }
830
+ return;
831
+ }
832
+ if (getEndOperator(operator) != null) {
833
+ this.angularOperatorStack.unshift(operator);
834
+ return;
835
+ }
836
+ var beginOperator = getBeginOperator(operator);
837
+ if (beginOperator == null) {
838
+ this.errorInParseLogic();
839
+ }
840
+ if (this.angularOperatorStack.length > 0) {
841
+ if (beginOperator == this.angularOperatorStack[0]) {
842
+ this.angularOperatorStack.shift();
843
+ return;
844
+ }
845
+ position = indexToLineAndColumn(this.text, this.index);
846
+ throw $interpolateMinErr('badexpr',
847
+ 'Unexpected operator “{0}” at line {1}, column {2} in text. Was expecting “{3}”. Text: “{4}”',
848
+ operator, position.line, position.column, getEndOperator(this.angularOperatorStack[0]), this.text);
849
+ }
850
+ // We are trying to pop off the operator stack but there really isn't anything to pop off.
851
+ this.index = match.index;
852
+ this.expressionFn = this.$parse(this.text.substring(this.expressionStartIndex, this.index));
853
+ // Needed to pretend to be $interpolate for tests copied from interpolateSpec.js
854
+ this.expressionFn['exp'] = this.text.substring(this.expressionStartIndex, this.index);
855
+ this.expressionFn['expressions'] = this.expressionFn['expressions'];
856
+ this.rule = null;
857
+ };
858
+
859
+ // NOTE: ADVANCED_OPTIMIZATIONS mode.
860
+ //
861
+ // This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
862
+ // constructs incompatible with that mode.
863
+
864
+ /* global $interpolateMinErr: false */
865
+ /* global MessageFormatParser: false */
866
+ /* global stringify: false */
867
+
868
+ /**
869
+ * @ngdoc service
870
+ * @name $$messageFormat
871
+ *
872
+ * @description
873
+ * Angular internal service to recognize MessageFormat extensions in interpolation expressions.
874
+ * For more information, see:
875
+ * https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
876
+ *
877
+ * ## Example
878
+ *
879
+ * <example name="ngMessageFormat-example" module="msgFmtExample" deps="angular-message-format.min.js">
880
+ * <file name="index.html">
881
+ * <div ng-controller="AppController">
882
+ * <button ng-click="decreaseRecipients()" id="decreaseRecipients">decreaseRecipients</button><br>
883
+ * <span>{{recipients.length, plural, offset:1
884
+ * =0 {{{sender.name}} gave no gifts (\#=#)}
885
+ * =1 {{{sender.name}} gave one gift to {{recipients[0].name}} (\#=#)}
886
+ * one {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
887
+ * other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
888
+ * }}</span>
889
+ * </div>
890
+ * </file>
891
+ *
892
+ * <file name="script.js">
893
+ * function Person(name, gender) {
894
+ * this.name = name;
895
+ * this.gender = gender;
896
+ * }
897
+ *
898
+ * var alice = new Person("Alice", "female"),
899
+ * bob = new Person("Bob", "male"),
900
+ * charlie = new Person("Charlie", "male"),
901
+ * harry = new Person("Harry Potter", "male");
902
+ *
903
+ * angular.module('msgFmtExample', ['ngMessageFormat'])
904
+ * .controller('AppController', ['$scope', function($scope) {
905
+ * $scope.recipients = [alice, bob, charlie];
906
+ * $scope.sender = harry;
907
+ * $scope.decreaseRecipients = function() {
908
+ * --$scope.recipients.length;
909
+ * };
910
+ * }]);
911
+ * </file>
912
+ *
913
+ * <file name="protractor.js" type="protractor">
914
+ * describe('MessageFormat plural', function() {
915
+ * it('should pluralize initial values', function() {
916
+ * var messageElem = element(by.binding('recipients.length')), decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
917
+ * expect(messageElem.getText()).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)');
918
+ * decreaseRecipientsBtn.click();
919
+ * expect(messageElem.getText()).toEqual('Harry Potter gave Alice and one other person a gift (#=1)');
920
+ * decreaseRecipientsBtn.click();
921
+ * expect(messageElem.getText()).toEqual('Harry Potter gave one gift to Alice (#=0)');
922
+ * decreaseRecipientsBtn.click();
923
+ * expect(messageElem.getText()).toEqual('Harry Potter gave no gifts (#=-1)');
924
+ * });
925
+ * });
926
+ * </file>
927
+ * </example>
928
+ */
929
+ var $$MessageFormatFactory = ['$parse', '$locale', '$sce', '$exceptionHandler', function $$messageFormat(
930
+ $parse, $locale, $sce, $exceptionHandler) {
931
+
932
+ function getStringifier(trustedContext, allOrNothing, text) {
933
+ return function stringifier(value) {
934
+ try {
935
+ value = trustedContext ? $sce['getTrusted'](trustedContext, value) : $sce['valueOf'](value);
936
+ return allOrNothing && (value === void 0) ? value : stringify(value);
937
+ } catch (err) {
938
+ $exceptionHandler($interpolateMinErr['interr'](text, err));
939
+ }
940
+ };
941
+ }
942
+
943
+ function interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
944
+ var stringifier = getStringifier(trustedContext, allOrNothing, text);
945
+ var parser = new MessageFormatParser(text, 0, $parse, $locale['pluralCat'], stringifier,
946
+ mustHaveExpression, trustedContext, allOrNothing);
947
+ parser.run(parser.ruleInterpolate);
948
+ return parser.parsedFn;
949
+ }
950
+
951
+ return {
952
+ 'interpolate': interpolate
953
+ };
954
+ }];
955
+
956
+ var $$interpolateDecorator = ['$$messageFormat', '$delegate', function $$interpolateDecorator($$messageFormat, $interpolate) {
957
+ if ($interpolate['startSymbol']() != "{{" || $interpolate['endSymbol']() != "}}") {
958
+ throw $interpolateMinErr('nochgmustache', 'angular-message-format.js currently does not allow you to use custom start and end symbols for interpolation.');
959
+ }
960
+ var interpolate = $$messageFormat['interpolate'];
961
+ interpolate['startSymbol'] = $interpolate['startSymbol'];
962
+ interpolate['endSymbol'] = $interpolate['endSymbol'];
963
+ return interpolate;
964
+ }];
965
+
966
+
967
+ /**
968
+ * @ngdoc module
969
+ * @name ngMessageFormat
970
+ * @packageName angular-message-format
971
+ * @description
972
+ */
973
+ var module = window['angular']['module']('ngMessageFormat', ['ng']);
974
+ module['factory']('$$messageFormat', $$MessageFormatFactory);
975
+ module['config'](['$provide', function($provide) {
976
+ $provide['decorator']('$interpolate', $$interpolateDecorator);
977
+ }]);
978
+
979
+
980
+ })(window, window.angular);