diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..11ffd1a
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,9 @@
+{
+ "presets": [
+ "es2015"
+ ],
+ "plugins": [
+ "add-module-exports",
+ "transform-es2015-modules-umd"
+ ]
+}
diff --git a/.npmignore b/.npmignore
index 0caa006..effc5fa 100644
--- a/.npmignore
+++ b/.npmignore
@@ -7,7 +7,6 @@
*.yml
*.xml
karma.conf.js
-test.sh
dist.sh
bower.json
src/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc238cd..ff56c55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+## 2.0.0 (2016-5-21)
+
+Features:
+ - Repackage library
+ - Package source files no longer duplicated
+
+Bugfixes:
+ - autoApply is undefined error
+ - arraybuffer data type
+
+BREAKING CHANGES:
+ - package source files are not duplicated
+ - if using files from `/`, use `/dist/` instead
+ - change `/angular-websocket-mock.js` to `/dist/angular-websocket-mock.js`
+ - change `/angular-websocket.js` to `/dist/angular-websocket.js`
+ - change `/angular-websocket-min.js` to `/dist/angular-websocket-min.js`
+ - change `/angular-websocket-min.js.map` to `/dist/angular-websocket-min.js.map`
+
+## 1.1.0 (2016-4-4)
+
+## 1.0.14 (2015-9-15)
+
## 1.0.13 (2015-6-2)
Build:
- UpdatedevDependencies
diff --git a/LICENSE b/LICENSE
index 8dada3e..4b60898 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,21 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+The MIT License (MIT)
+
+Copyright (c) 2013-2016 Patrick Stapleton, gdi2290, PatrickJS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 8e87451..9a18dc4 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
-# Angular Websocket [](https://angularclass.com/slack-join) [](https://gitter.im/gdi2290/angular-websocket?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://www.omniref.com/github/gdi2290/angular-websocket)
+# Angular Websocket [](https://angularclass.com/slack-join) [](https://gitter.im/AngularClass/angular-websocket?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [](https://www.omniref.com/github/gdi2290/angular-websocket)
[](https://travis-ci.org/gdi2290/angular-websocket)
[](https://github.com/gdi2290/angular-websocket)
@@ -24,9 +24,9 @@ You can download angular-websocket by:
* (prefered) Using bower and running `bower install angular-websocket --save`
* Using npm and running `npm install angular-websocket --save`
-* Downloading it manually by clicking [here to download development unminified version](https://raw.github.com/gdi2290/angular-websocket/master/angular-websocket.js)
-* CDN for development `https://rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.js`
-* CDN for production `https://cdn.rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.min.js`
+* Downloading it manually by clicking [here to download development unminified version](https://raw.github.com/AngularClass/angular-websocket/master/dist/angular-websocket.js)
+* CDN for development `https://cdn.rawgit.com/AngularClass/angular-websocket/v2.0.0/dist/angular-websocket.js`
+* CDN for production `https://cdn.rawgit.com/AngularClass/angular-websocket/v2.0.0/dist/angular-websocket.min.js`
## Usage
@@ -34,10 +34,8 @@ You can download angular-websocket by:
-
-
- {{ data }}
-
+
+
{{ data }}
-
+
//demo.label//
@@ -15248,6 +16618,15 @@ function $InterpolateProvider() {
return value;
}
+ //TODO: this is the same as the constantWatchDelegate in parse.js
+ function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
+ var unwatch;
+ return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
+ unwatch();
+ return constantInterp(scope);
+ }, listener, objectEquality);
+ }
+
/**
* @ngdoc service
* @name $interpolate
@@ -15343,6 +16722,19 @@ function $InterpolateProvider() {
* - `context`: evaluation context for all expressions embedded in the interpolated text
*/
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
+ // Provide a quick exit and simplified result function for text with no interpolation
+ if (!text.length || text.indexOf(startSymbol) === -1) {
+ var constantInterp;
+ if (!mustHaveExpression) {
+ var unescapedText = unescapeText(text);
+ constantInterp = valueFn(unescapedText);
+ constantInterp.exp = text;
+ constantInterp.expressions = [];
+ constantInterp.$$watchDelegate = constantWatchDelegate;
+ }
+ return constantInterp;
+ }
+
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
@@ -15479,8 +16871,8 @@ function $InterpolateProvider() {
}
function $IntervalProvider() {
- this.$get = ['$rootScope', '$window', '$q', '$$q',
- function($rootScope, $window, $q, $$q) {
+ this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
+ function($rootScope, $window, $q, $$q, $browser) {
var intervals = {};
@@ -15621,11 +17013,12 @@ function $IntervalProvider() {
count = isDefined(count) ? count : 0;
- promise.then(null, null, (!hasParams) ? fn : function() {
- fn.apply(null, args);
- });
-
promise.$$intervalId = setInterval(function tick() {
+ if (skipApply) {
+ $browser.defer(callback);
+ } else {
+ $rootScope.$evalAsync(callback);
+ }
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
@@ -15641,6 +17034,14 @@ function $IntervalProvider() {
intervals[promise.$$intervalId] = deferred;
return promise;
+
+ function callback() {
+ if (!hasParams) {
+ fn(iteration);
+ } else {
+ fn.apply(null, args);
+ }
+ }
}
@@ -16253,9 +17654,9 @@ var locationPrototype = {
* @description
* This method is getter / setter.
*
- * Return hash fragment when called without any parameter.
+ * Returns the hash fragment when called without any parameters.
*
- * Change hash fragment when called with parameter and return `$location`.
+ * Changes the hash fragment when called with a parameter and returns `$location`.
*
*
* ```js
@@ -16276,8 +17677,8 @@ var locationPrototype = {
* @name $location#replace
*
* @description
- * If called, all changes to $location during current `$digest` will be replacing current history
- * record, instead of adding new one.
+ * If called, all changes to $location during the current `$digest` will replace the current history
+ * record, instead of adding a new one.
*/
replace: function() {
this.$$replace = true;
@@ -16597,7 +17998,7 @@ function $LocationProvider() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
-
+ newUrl = trimEmptyHash(newUrl);
$location.$$parse(newUrl);
$location.$$state = newState;
@@ -16870,15 +18271,6 @@ var $parseMinErr = minErr('$parse');
function ensureSafeMemberName(name, fullExpression) {
- // From the JavaScript docs:
- // Property names must be strings. This means that non-string objects cannot be used
- // as keys in an object. Any non-string object, including a number, is typecasted
- // into a string via the toString method.
- //
- // So, to ensure that we are checking the same `name` that JavaScript would use,
- // we cast it to a string, if possible
- name = (isObject(name) && name.toString) ? name.toString() : name;
-
if (name === "__defineGetter__" || name === "__defineSetter__"
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|| name === "__proto__") {
@@ -16889,6 +18281,24 @@ function ensureSafeMemberName(name, fullExpression) {
return name;
}
+function getStringValue(name) {
+ // Property names must be strings. This means that non-string objects cannot be used
+ // as keys in an object. Any non-string object, including a number, is typecasted
+ // into a string via the toString method.
+ // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
+ //
+ // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
+ // to a string. It's not always possible. If `name` is an object and its `toString` method is
+ // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
+ //
+ // TypeError: Cannot convert object to primitive value
+ //
+ // For performance reasons, we don't catch this error here and allow it to propagate up the call
+ // stack. Note that you'll get the same error in JavaScript if you try to access a property using
+ // such a 'broken' object as a key.
+ return name + '';
+}
+
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj) {
@@ -16934,6 +18344,16 @@ function ensureSafeFunction(obj, fullExpression) {
}
}
+function ensureSafeAssignContext(obj, fullExpression) {
+ if (obj) {
+ if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
+ obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
+ throw $parseMinErr('isecaf',
+ 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
+ }
+ }
+}
+
var OPERATORS = createMap();
forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
@@ -16963,7 +18383,7 @@ Lexer.prototype = {
this.readString(ch);
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
this.readNumber();
- } else if (this.isIdent(ch)) {
+ } else if (this.isIdentifierStart(this.peekMultichar())) {
this.readIdent();
} else if (this.is(ch, '(){}[].,;:?')) {
this.tokens.push({index: this.index, text: ch});
@@ -17007,12 +18427,49 @@ Lexer.prototype = {
ch === '\n' || ch === '\v' || ch === '\u00A0');
},
- isIdent: function(ch) {
+ isIdentifierStart: function(ch) {
+ return this.options.isIdentifierStart ?
+ this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
+ this.isValidIdentifierStart(ch);
+ },
+
+ isValidIdentifierStart: function(ch) {
return ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' === ch || ch === '$');
},
+ isIdentifierContinue: function(ch) {
+ return this.options.isIdentifierContinue ?
+ this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
+ this.isValidIdentifierContinue(ch);
+ },
+
+ isValidIdentifierContinue: function(ch, cp) {
+ return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
+ },
+
+ codePointAt: function(ch) {
+ if (ch.length === 1) return ch.charCodeAt(0);
+ /*jshint bitwise: false*/
+ return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
+ /*jshint bitwise: true*/
+ },
+
+ peekMultichar: function() {
+ var ch = this.text.charAt(this.index);
+ var peek = this.peek();
+ if (!peek) {
+ return ch;
+ }
+ var cp1 = ch.charCodeAt(0);
+ var cp2 = peek.charCodeAt(0);
+ if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
+ return ch + peek;
+ }
+ return ch;
+ },
+
isExpOperator: function(ch) {
return (ch === '-' || ch === '+' || this.isNumber(ch));
},
@@ -17061,12 +18518,13 @@ Lexer.prototype = {
readIdent: function() {
var start = this.index;
+ this.index += this.peekMultichar().length;
while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (!(this.isIdent(ch) || this.isNumber(ch))) {
+ var ch = this.peekMultichar();
+ if (!this.isIdentifierContinue(ch)) {
break;
}
- this.index++;
+ this.index += ch.length;
}
this.tokens.push({
index: start,
@@ -17137,6 +18595,7 @@ AST.ArrayExpression = 'ArrayExpression';
AST.Property = 'Property';
AST.ObjectExpression = 'ObjectExpression';
AST.ThisExpression = 'ThisExpression';
+AST.LocalsExpression = 'LocalsExpression';
// Internal use only
AST.NGValueParameter = 'NGValueParameter';
@@ -17275,8 +18734,10 @@ AST.prototype = {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
- } else if (this.constants.hasOwnProperty(this.peek().text)) {
- primary = copy(this.constants[this.consume().text]);
+ } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
+ primary = copy(this.selfReferential[this.consume().text]);
+ } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
+ primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
} else if (this.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
@@ -17428,16 +18889,9 @@ AST.prototype = {
return false;
},
-
- /* `undefined` is not a constant, it is an identifier,
- * but using it as an identifier is not supported
- */
- constants: {
- 'true': { type: AST.Literal, value: true },
- 'false': { type: AST.Literal, value: false },
- 'null': { type: AST.Literal, value: null },
- 'undefined': {type: AST.Literal, value: undefined },
- 'this': {type: AST.ThisExpression }
+ selfReferential: {
+ 'this': {type: AST.ThisExpression },
+ '$locals': {type: AST.LocalsExpression }
}
};
@@ -17557,6 +19011,10 @@ function findConstantAndWatchExpressions(ast, $filter) {
ast.constant = false;
ast.toWatch = [];
break;
+ case AST.LocalsExpression:
+ ast.constant = false;
+ ast.toWatch = [];
+ break;
}
}
@@ -17648,6 +19106,8 @@ ASTCompiler.prototype = {
'ensureSafeMemberName',
'ensureSafeObject',
'ensureSafeFunction',
+ 'getStringValue',
+ 'ensureSafeAssignContext',
'ifDefined',
'plus',
'text',
@@ -17656,6 +19116,8 @@ ASTCompiler.prototype = {
ensureSafeMemberName,
ensureSafeObject,
ensureSafeFunction,
+ getStringValue,
+ ensureSafeAssignContext,
ifDefined,
plusFn,
expression);
@@ -17796,9 +19258,13 @@ ASTCompiler.prototype = {
intoId = intoId || this.nextId();
self.recurse(ast.object, left, undefined, function() {
self.if_(self.notNull(left), function() {
+ if (create && create !== 1) {
+ self.addEnsureSafeAssignContext(left);
+ }
if (ast.computed) {
right = self.nextId();
self.recurse(ast.property, right);
+ self.getStringValue(right);
self.addEnsureSafeMemberName(right);
if (create && create !== 1) {
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
@@ -17876,12 +19342,13 @@ ASTCompiler.prototype = {
right = this.nextId();
left = {};
if (!isAssignable(ast.left)) {
- throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
+ throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
}
this.recurse(ast.left, undefined, left, function() {
self.if_(self.notNull(left.context), function() {
self.recurse(ast.right, right);
self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
+ self.addEnsureSafeAssignContext(left.context);
expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
self.assign(intoId, expression);
recursionFn(intoId || expression);
@@ -17917,6 +19384,10 @@ ASTCompiler.prototype = {
this.assign(intoId, 's');
recursionFn('s');
break;
+ case AST.LocalsExpression:
+ this.assign(intoId, 'l');
+ recursionFn('l');
+ break;
case AST.NGValueParameter:
this.assign(intoId, 'v');
recursionFn('v');
@@ -17983,7 +19454,13 @@ ASTCompiler.prototype = {
},
nonComputedMember: function(left, right) {
- return left + '.' + right;
+ var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
+ var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
+ if (SAFE_IDENTIFIER.test(right)) {
+ return left + '.' + right;
+ } else {
+ return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
+ }
},
computedMember: function(left, right) {
@@ -18007,6 +19484,10 @@ ASTCompiler.prototype = {
this.current().body.push(this.ensureSafeFunction(item), ';');
},
+ addEnsureSafeAssignContext: function(item) {
+ this.current().body.push(this.ensureSafeAssignContext(item), ';');
+ },
+
ensureSafeObject: function(item) {
return 'ensureSafeObject(' + item + ',text)';
},
@@ -18019,6 +19500,14 @@ ASTCompiler.prototype = {
return 'ensureSafeFunction(' + item + ',text)';
},
+ getStringValue: function(item) {
+ this.assign(item, 'getStringValue(' + item + ')');
+ },
+
+ ensureSafeAssignContext: function(item) {
+ return 'ensureSafeAssignContext(' + item + ',text)';
+ },
+
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
var self = this;
return function() {
@@ -18096,7 +19585,7 @@ ASTInterpreter.prototype = {
forEach(ast.body, function(expression) {
expressions.push(self.recurse(expression.expression));
});
- var fn = ast.body.length === 0 ? function() {} :
+ var fn = ast.body.length === 0 ? noop :
ast.body.length === 1 ? expressions[0] :
function(scope, locals) {
var lastValue;
@@ -18196,6 +19685,7 @@ ASTInterpreter.prototype = {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
ensureSafeObject(lhs.value, self.expression);
+ ensureSafeAssignContext(lhs.context);
lhs.context[lhs.name] = rhs;
return context ? {value: rhs} : rhs;
};
@@ -18231,8 +19721,12 @@ ASTInterpreter.prototype = {
return function(scope) {
return context ? {value: scope} : scope;
};
+ case AST.LocalsExpression:
+ return function(scope, locals) {
+ return context ? {value: locals} : locals;
+ };
case AST.NGValueParameter:
- return function(scope, locals, assign, inputs) {
+ return function(scope, locals, assign) {
return context ? {value: assign} : assign;
};
}
@@ -18393,9 +19887,13 @@ ASTInterpreter.prototype = {
var value;
if (lhs != null) {
rhs = right(scope, locals, assign, inputs);
+ rhs = getStringValue(rhs);
ensureSafeMemberName(rhs, expression);
- if (create && create !== 1 && lhs && !(lhs[rhs])) {
- lhs[rhs] = {};
+ if (create && create !== 1) {
+ ensureSafeAssignContext(lhs);
+ if (lhs && !(lhs[rhs])) {
+ lhs[rhs] = {};
+ }
}
value = lhs[rhs];
ensureSafeObject(value, expression);
@@ -18410,8 +19908,11 @@ ASTInterpreter.prototype = {
nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
- if (create && create !== 1 && lhs && !(lhs[right])) {
- lhs[right] = {};
+ if (create && create !== 1) {
+ ensureSafeAssignContext(lhs);
+ if (lhs && !(lhs[right])) {
+ lhs[right] = {};
+ }
}
var value = lhs != null ? lhs[right] : undefined;
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
@@ -18439,7 +19940,7 @@ var Parser = function(lexer, $filter, options) {
this.lexer = lexer;
this.$filter = $filter;
this.options = options;
- this.ast = new AST(this.lexer);
+ this.ast = new AST(lexer, options);
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
new ASTCompiler(this.ast, $filter);
};
@@ -18452,9 +19953,6 @@ Parser.prototype = {
}
};
-var getterFnCacheDefault = createMap();
-var getterFnCacheExpensive = createMap();
-
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
@@ -18519,21 +20017,87 @@ function getValueOf(value) {
function $ParseProvider() {
var cacheDefault = createMap();
var cacheExpensive = createMap();
+ var literals = {
+ 'true': true,
+ 'false': false,
+ 'null': null,
+ 'undefined': undefined
+ };
+ var identStart, identContinue;
+
+ /**
+ * @ngdoc method
+ * @name $parseProvider#addLiteral
+ * @description
+ *
+ * Configure $parse service to add literal values that will be present as literal at expressions.
+ *
+ * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
+ * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
+ *
+ **/
+ this.addLiteral = function(literalName, literalValue) {
+ literals[literalName] = literalValue;
+ };
+
+ /**
+ * @ngdoc method
+ * @name $parseProvider#setIdentifierFns
+ * @description
+ *
+ * Allows defining the set of characters that are allowed in Angular expressions. The function
+ * `identifierStart` will get called to know if a given character is a valid character to be the
+ * first character for an identifier. The function `identifierContinue` will get called to know if
+ * a given character is a valid character to be a follow-up identifier character. The functions
+ * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
+ * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
+ * mind that the `string` parameter can be two characters long depending on the character
+ * representation. It is expected for the function to return `true` or `false`, whether that
+ * character is allowed or not.
+ *
+ * Since this function will be called extensivelly, keep the implementation of these functions fast,
+ * as the performance of these functions have a direct impact on the expressions parsing speed.
+ *
+ * @param {function=} identifierStart The function that will decide whether the given character is
+ * a valid identifier start character.
+ * @param {function=} identifierContinue The function that will decide whether the given character is
+ * a valid identifier continue character.
+ */
+ this.setIdentifierFns = function(identifierStart, identifierContinue) {
+ identStart = identifierStart;
+ identContinue = identifierContinue;
+ return this;
+ };
this.$get = ['$filter', function($filter) {
var noUnsafeEval = csp().noUnsafeEval;
var $parseOptions = {
csp: noUnsafeEval,
- expensiveChecks: false
+ expensiveChecks: false,
+ literals: copy(literals),
+ isIdentifierStart: isFunction(identStart) && identStart,
+ isIdentifierContinue: isFunction(identContinue) && identContinue
},
$parseOptionsExpensive = {
csp: noUnsafeEval,
- expensiveChecks: true
+ expensiveChecks: true,
+ literals: copy(literals),
+ isIdentifierStart: isFunction(identStart) && identStart,
+ isIdentifierContinue: isFunction(identContinue) && identContinue
};
+ var runningChecksEnabled = false;
- return function $parse(exp, interceptorFn, expensiveChecks) {
+ $parse.$$runningExpensiveChecks = function() {
+ return runningChecksEnabled;
+ };
+
+ return $parse;
+
+ function $parse(exp, interceptorFn, expensiveChecks) {
var parsedExpression, oneTime, cacheKey;
+ expensiveChecks = expensiveChecks || runningChecksEnabled;
+
switch (typeof exp) {
case 'string':
exp = exp.trim();
@@ -18559,6 +20123,9 @@ function $ParseProvider() {
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
+ if (expensiveChecks) {
+ parsedExpression = expensiveChecksInterceptor(parsedExpression);
+ }
cache[cacheKey] = parsedExpression;
}
return addInterceptor(parsedExpression, interceptorFn);
@@ -18567,9 +20134,33 @@ function $ParseProvider() {
return addInterceptor(exp, interceptorFn);
default:
- return noop;
+ return addInterceptor(noop, interceptorFn);
}
- };
+ }
+
+ function expensiveChecksInterceptor(fn) {
+ if (!fn) return fn;
+ expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
+ expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
+ expensiveCheckFn.constant = fn.constant;
+ expensiveCheckFn.literal = fn.literal;
+ for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
+ fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
+ }
+ expensiveCheckFn.inputs = fn.inputs;
+
+ return expensiveCheckFn;
+
+ function expensiveCheckFn(scope, locals, assign, inputs) {
+ var expensiveCheckOldValue = runningChecksEnabled;
+ runningChecksEnabled = true;
+ try {
+ return fn(scope, locals, assign, inputs);
+ } finally {
+ runningChecksEnabled = expensiveCheckOldValue;
+ }
+ }
+ }
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
@@ -18686,25 +20277,22 @@ function $ParseProvider() {
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function constantWatch(scope) {
- return parsedExpression(scope);
- }, function constantListener(value, old, scope) {
- if (isFunction(listener)) {
- listener.apply(this, arguments);
- }
unwatch();
- }, objectEquality);
+ return parsedExpression(scope);
+ }, listener, objectEquality);
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
+ var useInputs = false;
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
- var value = parsedExpression(scope, locals, assign, inputs);
+ var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
@@ -18722,6 +20310,7 @@ function $ParseProvider() {
// If there is an interceptor, but no watchDelegate then treat the interceptor like
// we treat filters - it is assumed to be a pure function unless flagged with $stateful
fn.$$watchDelegate = inputsWatchDelegate;
+ useInputs = !parsedExpression.inputs;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
@@ -18743,15 +20332,15 @@ function $ParseProvider() {
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
- * implementations, and the other which resembles ES6 promises to some degree.
+ * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
*
* # $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
- * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
+ * function as the first argument. This is similar to the native Promise implementation from ES6,
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
*
- * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
+ * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
* available yet.
*
* It can be used like so:
@@ -18783,6 +20372,8 @@ function $ParseProvider() {
*
* Note: progress/notify callbacks are not currently supported via the ES6-style interface.
*
+ * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
+ *
* However, the more traditional CommonJS-style usage is still available, and documented below.
*
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
@@ -18914,7 +20505,7 @@ function $ParseProvider() {
* - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks.
*
- * # Testing
+ * # Testing
*
* ```js
* it('should simulate promise', inject(function($q, $rootScope) {
@@ -18971,18 +20562,6 @@ function $$QProvider() {
*/
function qFactory(nextTick, exceptionHandler) {
var $qMinErr = minErr('$q', TypeError);
- function callOnce(self, resolveFn, rejectFn) {
- var called = false;
- function wrap(fn) {
- return function(value) {
- if (called) return;
- called = true;
- fn.call(self, value);
- };
- }
-
- return [wrap(resolveFn), wrap(rejectFn)];
- }
/**
* @ngdoc method
@@ -18995,7 +20574,12 @@ function qFactory(nextTick, exceptionHandler) {
* @returns {Deferred} Returns a new instance of deferred.
*/
var defer = function() {
- return new Deferred();
+ var d = new Deferred();
+ //Necessary to support unbound execution :/
+ d.resolve = simpleBind(d, d.resolve);
+ d.reject = simpleBind(d, d.reject);
+ d.notify = simpleBind(d, d.notify);
+ return d;
};
function Promise() {
@@ -19068,10 +20652,6 @@ function qFactory(nextTick, exceptionHandler) {
function Deferred() {
this.promise = new Promise();
- //Necessary to support unbound execution :/
- this.resolve = simpleBind(this, this.resolve);
- this.reject = simpleBind(this, this.reject);
- this.notify = simpleBind(this, this.notify);
}
extend(Deferred.prototype, {
@@ -19089,23 +20669,34 @@ function qFactory(nextTick, exceptionHandler) {
},
$$resolve: function(val) {
- var then, fns;
-
- fns = callOnce(this, this.$$resolve, this.$$reject);
+ var then;
+ var that = this;
+ var done = false;
try {
if ((isObject(val) || isFunction(val))) then = val && val.then;
if (isFunction(then)) {
this.promise.$$state.status = -1;
- then.call(val, fns[0], fns[1], this.notify);
+ then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
} else {
this.promise.$$state.value = val;
this.promise.$$state.status = 1;
scheduleProcessQueue(this.promise.$$state);
}
} catch (e) {
- fns[1](e);
+ rejectPromise(e);
exceptionHandler(e);
}
+
+ function resolvePromise(val) {
+ if (done) return;
+ done = true;
+ that.$$resolve(val);
+ }
+ function rejectPromise(val) {
+ if (done) return;
+ done = true;
+ that.$$reject(val);
+ }
},
reject: function(reason) {
@@ -19294,11 +20885,6 @@ function qFactory(nextTick, exceptionHandler) {
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
}
- if (!(this instanceof Q)) {
- // More useful when $Q is the Promise itself.
- return new Q(resolver);
- }
-
var deferred = new Deferred();
function resolveFn(value) {
@@ -19314,6 +20900,10 @@ function qFactory(nextTick, exceptionHandler) {
return deferred.promise;
};
+ // Let's make the instanceof operator work for promises, so that
+ // `new $q(fn) instanceof $q` would evaluate to true.
+ $Q.prototype = Promise.prototype;
+
$Q.defer = defer;
$Q.reject = reject;
$Q.when = when;
@@ -19367,15 +20957,15 @@ function $$RAFProvider() { //rAF
* exposed as $$____ properties
*
* Loop operations are optimized by using while(count--) { ... }
- * - this means that in order to keep the same order of execution as addition we have to add
+ * - This means that in order to keep the same order of execution as addition we have to add
* items to the array at the beginning (unshift) instead of at the end (push)
*
* Child scopes are created and removed often
- * - Using an array would be slow since inserts in middle are expensive so we use linked list
+ * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
*
- * There are few watches then a lot of observers. This is why you don't want the observer to be
- * implemented in the same way as watch. Watch requires return of initialization function which
- * are expensive to construct.
+ * There are fewer watches than observers. This is why you don't want the observer to be implemented
+ * in the same way as watch. Watch requires return of the initialization function which is expensive
+ * to construct.
*/
@@ -19417,7 +21007,7 @@ function $$RAFProvider() { //rAF
* Every application has a single root {@link ng.$rootScope.Scope scope}.
* All other scopes are descendant scopes of the root scope. Scopes provide separation
* between the model and the view, via a mechanism for watching the model for changes.
- * They also provide an event emission/broadcast and subscription facility. See the
+ * They also provide event emission/broadcast and subscription facility. See the
* {@link guide/scope developer guide on scopes}.
*/
function $RootScopeProvider() {
@@ -19447,13 +21037,36 @@ function $RootScopeProvider() {
return ChildScope;
}
- this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
- function($injector, $exceptionHandler, $parse, $browser) {
+ this.$get = ['$exceptionHandler', '$parse', '$browser',
+ function($exceptionHandler, $parse, $browser) {
function destroyChildScope($event) {
$event.currentScope.$$destroyed = true;
}
+ function cleanUpScope($scope) {
+
+ if (msie === 9) {
+ // There is a memory leak in IE9 if all child scopes are not disconnected
+ // completely when a scope is destroyed. So this code will recurse up through
+ // all this scopes children
+ //
+ // See issue https://github.com/angular/angular.js/issues/10706
+ $scope.$$childHead && cleanUpScope($scope.$$childHead);
+ $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
+ }
+
+ // The code below works around IE9 and V8's memory leaks
+ //
+ // See:
+ // - https://code.google.com/p/v8/issues/detail?id=2073#c26
+ // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
+ // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
+
+ $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
+ $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
+ }
+
/**
* @ngdoc type
* @name $rootScope.Scope
@@ -19709,7 +21322,7 @@ function $RootScopeProvider() {
* - `newVal` contains the current value of the `watchExpression`
* - `oldVal` contains the previous value of the `watchExpression`
* - `scope` refers to the current scope
- * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
+ * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
* comparing for reference equality.
* @returns {function()} Returns a deregistration function for this listener.
*/
@@ -20074,13 +21687,13 @@ function $RootScopeProvider() {
*
*/
$digest: function() {
- var watch, value, last,
+ var watch, value, last, fn, get,
watchers,
length,
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
- logIdx, logMsg, asyncTask;
+ logIdx, asyncTask;
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
@@ -20120,7 +21733,8 @@ function $RootScopeProvider() {
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if (watch) {
- if ((value = watch.get(current)) !== (last = watch.last) &&
+ get = watch.get;
+ if ((value = get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value === 'number' && typeof last === 'number'
@@ -20128,7 +21742,8 @@ function $RootScopeProvider() {
dirty = true;
lastDirtyWatch = watch;
watch.last = watch.eq ? copy(value, null) : value;
- watch.fn(value, ((last === initWatchVal) ? value : last), current);
+ fn = watch.fn;
+ fn(value, ((last === initWatchVal) ? value : last), current);
if (ttl < 5) {
logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = [];
@@ -20250,16 +21865,9 @@ function $RootScopeProvider() {
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
this.$$listeners = {};
- // All of the code below is bogus code that works around V8's memory leak via optimized code
- // and inline caches.
- //
- // see:
- // - https://code.google.com/p/v8/issues/detail?id=2073#c26
- // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
- // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
-
- this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
- this.$$childTail = this.$root = this.$$watchers = null;
+ // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
+ this.$$nextSibling = null;
+ cleanUpScope(this);
},
/**
@@ -20335,7 +21943,7 @@ function $RootScopeProvider() {
});
}
- asyncQueue.push({scope: this, expression: expr, locals: locals});
+ asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
},
$$postDigest: function(fn) {
@@ -20427,6 +22035,7 @@ function $RootScopeProvider() {
$applyAsync: function(expr) {
var scope = this;
expr && applyAsyncQueue.push($applyAsyncExpression);
+ expr = $parse(expr);
scheduleApplyAsync();
function $applyAsyncExpression() {
@@ -20701,6 +22310,21 @@ function $RootScopeProvider() {
}];
}
+/**
+ * @ngdoc service
+ * @name $rootElement
+ *
+ * @description
+ * The root element of Angular application. This is either the element where {@link
+ * ng.directive:ngApp ngApp} was declared or the element passed into
+ * {@link angular.bootstrap}. The element represents the root element of application. It is also the
+ * location where the application's {@link auto.$injector $injector} service gets
+ * published, and can be retrieved using `$rootElement.injector()`.
+ */
+
+
+// the implementation is in angular.bootstrap
+
/**
* @description
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
@@ -20915,13 +22539,15 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.
*
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.
*
- * Note: **an empty whitelist array will block all URLs**!
+ *
+ * **Note:** an empty whitelist array will block all URLs!
+ *
*
* @return {Array} the currently set whitelist array.
*
@@ -20944,17 +22570,17 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.
*
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.
*
- * The typical usage for the blacklist is to **block
- * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
- * these would otherwise be trusted but actually return content from the redirected domain.
+ * The typical usage for the blacklist is to **block
+ * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
+ * these would otherwise be trusted but actually return content from the redirected domain.
*
- * Finally, **the blacklist overrides the whitelist** and has the final say.
+ * Finally, **the blacklist overrides the whitelist** and has the final say.
*
* @return {Array} the currently set blacklist array.
*
@@ -21113,6 +22739,11 @@ function $SceDelegateProvider() {
* returns the originally supplied value if the queried context type is a supertype of the
* created type. If this condition isn't satisfied, throws an exception.
*
+ *
+ * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
+ * (XSS) vulnerability in your application.
+ *
+ *
* @param {string} type The kind of context in which this value is to be used.
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
* `$sceDelegate.trustAs`} call.
@@ -21255,7 +22886,7 @@ function $SceDelegateProvider() {
* By default, Angular only loads templates from the same domain and protocol as the application
* document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
- * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
+ * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
* them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
*
* *Please note*:
@@ -21847,6 +23478,10 @@ function $SceProvider() {
function $SnifferProvider() {
this.$get = ['$window', '$document', function($window, $document) {
var eventSupport = {},
+ // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
+ // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
+ isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
+ hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
android =
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -21891,7 +23526,7 @@ function $SnifferProvider() {
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
// jshint -W018
- history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
+ history: !!(hasHistoryPushState && !(android < 4) && !boxee),
// jshint +W018
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
@@ -21917,29 +23552,66 @@ function $SnifferProvider() {
}];
}
-var $compileMinErr = minErr('$compile');
+var $templateRequestMinErr = minErr('$compile');
/**
- * @ngdoc service
- * @name $templateRequest
- *
+ * @ngdoc provider
+ * @name $templateRequestProvider
* @description
- * The `$templateRequest` service runs security checks then downloads the provided template using
- * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
- * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
- * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
- * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
- * when `tpl` is of type string and `$templateCache` has the matching entry.
- *
- * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
- * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
- *
- * @return {Promise} a promise for the HTTP response data of the given URL.
+ * Used to configure the options passed to the {@link $http} service when making a template request.
*
- * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
+ * For example, it can be used for specifying the "Accept" header that is sent to the server, when
+ * requesting a template.
*/
function $TemplateRequestProvider() {
+
+ var httpOptions;
+
+ /**
+ * @ngdoc method
+ * @name $templateRequestProvider#httpOptions
+ * @description
+ * The options to be passed to the {@link $http} service when making the request.
+ * You can use this to override options such as the "Accept" header for template requests.
+ *
+ * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
+ * options if not overridden here.
+ *
+ * @param {string=} value new value for the {@link $http} options.
+ * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
+ */
+ this.httpOptions = function(val) {
+ if (val) {
+ httpOptions = val;
+ return this;
+ }
+ return httpOptions;
+ };
+
+ /**
+ * @ngdoc service
+ * @name $templateRequest
+ *
+ * @description
+ * The `$templateRequest` service runs security checks then downloads the provided template using
+ * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
+ * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
+ * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
+ * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
+ * when `tpl` is of type string and `$templateCache` has the matching entry.
+ *
+ * If you want to pass custom options to the `$http` service, such as setting the Accept header you
+ * can configure this via {@link $templateRequestProvider#httpOptions}.
+ *
+ * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
+ * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
+ *
+ * @return {Promise} a promise for the HTTP response data of the given URL.
+ *
+ * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
+ */
this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
+
function handleRequestFn(tpl, ignoreRequestError) {
handleRequestFn.totalPendingRequests++;
@@ -21962,12 +23634,10 @@ function $TemplateRequestProvider() {
transformResponse = null;
}
- var httpOptions = {
- cache: $templateCache,
- transformResponse: transformResponse
- };
-
- return $http.get(tpl, httpOptions)
+ return $http.get(tpl, extend({
+ cache: $templateCache,
+ transformResponse: transformResponse
+ }, httpOptions))
['finally'](function() {
handleRequestFn.totalPendingRequests--;
})
@@ -21978,7 +23648,7 @@ function $TemplateRequestProvider() {
function handleError(resp) {
if (!ignoreRequestError) {
- throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
+ throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
tpl, resp.status, resp.statusText);
}
return $q.reject(resp);
@@ -22138,8 +23808,8 @@ function $TimeoutProvider() {
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @param {...*=} Pass additional parameters to the executed function.
- * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
- * promise will be resolved with is the return value of the `fn` function.
+ * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
+ * will be resolved with the return value of the `fn` function.
*
*/
function timeout(fn, delay, invokeApply) {
@@ -22208,7 +23878,7 @@ function $TimeoutProvider() {
// doesn't know about mocked locations and resolves URLs to the real document - which is
// exactly the behavior needed here. There is little value is mocking these out for this
// service.
-var urlParsingNode = document.createElement("a");
+var urlParsingNode = window.document.createElement("a");
var originUrl = urlResolve(window.location.href);
@@ -22815,6 +24485,10 @@ function getTypeForFilter(val) {
return (val === null) ? 'null' : typeof val;
}
+var MAX_DIGITS = 22;
+var DECIMAL_SEP = '.';
+var ZERO_CHAR = '0';
+
/**
* @ngdoc filter
* @name currency
@@ -22896,7 +24570,7 @@ function currencyFilter($locale) {
* Formats a number as text.
*
* If the input is null or undefined, it will just be returned.
- * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
+ * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
* If the input is not a number an empty string is returned.
*
*
@@ -22904,7 +24578,9 @@ function currencyFilter($locale) {
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
* If this is not provided then the fraction size is computed from the current locale's number
* formatting pattern. In the case of the default locale, it will be 3.
- * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
+ * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
+ * locale (e.g., in the en_US locale it will have "." as the decimal separator and
+ * include "," group separators after each third digit).
*
* @example
@@ -22939,8 +24615,6 @@ function currencyFilter($locale) {
*/
-
-
numberFilter.$inject = ['$locale'];
function numberFilter($locale) {
var formats = $locale.NUMBER_FORMATS;
@@ -22954,102 +24628,227 @@ function numberFilter($locale) {
};
}
-var DECIMAL_SEP = '.';
-function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
- if (isObject(number)) return '';
-
- var isNegative = number < 0;
- number = Math.abs(number);
+/**
+ * Parse a number (as a string) into three components that can be used
+ * for formatting the number.
+ *
+ * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
+ *
+ * @param {string} numStr The number to parse
+ * @return {object} An object describing this number, containing the following keys:
+ * - d : an array of digits containing leading zeros as necessary
+ * - i : the number of the digits in `d` that are to the left of the decimal point
+ * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
+ *
+ */
+function parse(numStr) {
+ var exponent = 0, digits, numberOfIntegerDigits;
+ var i, j, zeros;
- var isInfinity = number === Infinity;
- if (!isInfinity && !isFinite(number)) return '';
+ // Decimal point?
+ if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
+ numStr = numStr.replace(DECIMAL_SEP, '');
+ }
- var numStr = number + '',
- formatedText = '',
- hasExponent = false,
- parts = [];
+ // Exponential form?
+ if ((i = numStr.search(/e/i)) > 0) {
+ // Work out the exponent.
+ if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
+ numberOfIntegerDigits += +numStr.slice(i + 1);
+ numStr = numStr.substring(0, i);
+ } else if (numberOfIntegerDigits < 0) {
+ // There was no decimal point or exponent so it is an integer.
+ numberOfIntegerDigits = numStr.length;
+ }
- if (isInfinity) formatedText = '\u221e';
+ // Count the number of leading zeros.
+ for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
- if (!isInfinity && numStr.indexOf('e') !== -1) {
- var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
- if (match && match[2] == '-' && match[3] > fractionSize + 1) {
- number = 0;
- } else {
- formatedText = numStr;
- hasExponent = true;
+ if (i == (zeros = numStr.length)) {
+ // The digits are all zero.
+ digits = [0];
+ numberOfIntegerDigits = 1;
+ } else {
+ // Count the number of trailing zeros
+ zeros--;
+ while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
+
+ // Trailing zeros are insignificant so ignore them
+ numberOfIntegerDigits -= i;
+ digits = [];
+ // Convert string to array of digits without leading/trailing zeros.
+ for (j = 0; i <= zeros; i++, j++) {
+ digits[j] = +numStr.charAt(i);
}
}
- if (!isInfinity && !hasExponent) {
- var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
+ // If the number overflows the maximum allowed digits then use an exponent.
+ if (numberOfIntegerDigits > MAX_DIGITS) {
+ digits = digits.splice(0, MAX_DIGITS - 1);
+ exponent = numberOfIntegerDigits - 1;
+ numberOfIntegerDigits = 1;
+ }
- // determine fractionSize if it is not specified
- if (isUndefined(fractionSize)) {
- fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
- }
+ return { d: digits, e: exponent, i: numberOfIntegerDigits };
+}
- // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
- // inspired by:
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
- number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
+/**
+ * Round the parsed number to the specified number of decimal places
+ * This function changed the parsedNumber in-place
+ */
+function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
+ var digits = parsedNumber.d;
+ var fractionLen = digits.length - parsedNumber.i;
- var fraction = ('' + number).split(DECIMAL_SEP);
- var whole = fraction[0];
- fraction = fraction[1] || '';
+ // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
+ fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
- var i, pos = 0,
- lgroup = pattern.lgSize,
- group = pattern.gSize;
+ // The index of the digit to where rounding is to occur
+ var roundAt = fractionSize + parsedNumber.i;
+ var digit = digits[roundAt];
- if (whole.length >= (lgroup + group)) {
- pos = whole.length - lgroup;
- for (i = 0; i < pos; i++) {
- if ((pos - i) % group === 0 && i !== 0) {
- formatedText += groupSep;
- }
- formatedText += whole.charAt(i);
+ if (roundAt > 0) {
+ // Drop fractional digits beyond `roundAt`
+ digits.splice(Math.max(parsedNumber.i, roundAt));
+
+ // Set non-fractional digits beyond `roundAt` to 0
+ for (var j = roundAt; j < digits.length; j++) {
+ digits[j] = 0;
}
+ } else {
+ // We rounded to zero so reset the parsedNumber
+ fractionLen = Math.max(0, fractionLen);
+ parsedNumber.i = 1;
+ digits.length = Math.max(1, roundAt = fractionSize + 1);
+ digits[0] = 0;
+ for (var i = 1; i < roundAt; i++) digits[i] = 0;
}
- for (i = pos; i < whole.length; i++) {
- if ((whole.length - i) % lgroup === 0 && i !== 0) {
- formatedText += groupSep;
+ if (digit >= 5) {
+ if (roundAt - 1 < 0) {
+ for (var k = 0; k > roundAt; k--) {
+ digits.unshift(0);
+ parsedNumber.i++;
+ }
+ digits.unshift(1);
+ parsedNumber.i++;
+ } else {
+ digits[roundAt - 1]++;
}
- formatedText += whole.charAt(i);
}
- // format fraction part.
- while (fraction.length < fractionSize) {
- fraction += '0';
+ // Pad out with zeros to get the required fraction length
+ for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
+
+
+ // Do any carrying, e.g. a digit was rounded up to 10
+ var carry = digits.reduceRight(function(carry, d, i, digits) {
+ d = d + carry;
+ digits[i] = d % 10;
+ return Math.floor(d / 10);
+ }, 0);
+ if (carry) {
+ digits.unshift(carry);
+ parsedNumber.i++;
}
+}
+
+/**
+ * Format a number into a string
+ * @param {number} number The number to format
+ * @param {{
+ * minFrac, // the minimum number of digits required in the fraction part of the number
+ * maxFrac, // the maximum number of digits required in the fraction part of the number
+ * gSize, // number of digits in each group of separated digits
+ * lgSize, // number of digits in the last group of digits before the decimal separator
+ * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
+ * posPre, // the string to go in front of a positive number
+ * negSuf, // the string to go after a negative number (e.g. `)`)
+ * posSuf // the string to go after a positive number
+ * }} pattern
+ * @param {string} groupSep The string to separate groups of number (e.g. `,`)
+ * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
+ * @param {[type]} fractionSize The size of the fractional part of the number
+ * @return {string} The number formatted as a string
+ */
+function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
+
+ if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
- if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
+ var isInfinity = !isFinite(number);
+ var isZero = false;
+ var numStr = Math.abs(number) + '',
+ formattedText = '',
+ parsedNumber;
+
+ if (isInfinity) {
+ formattedText = '\u221e';
} else {
- if (fractionSize > 0 && number < 1) {
- formatedText = number.toFixed(fractionSize);
- number = parseFloat(formatedText);
+ parsedNumber = parse(numStr);
+
+ roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
+
+ var digits = parsedNumber.d;
+ var integerLen = parsedNumber.i;
+ var exponent = parsedNumber.e;
+ var decimals = [];
+ isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
+
+ // pad zeros for small numbers
+ while (integerLen < 0) {
+ digits.unshift(0);
+ integerLen++;
}
- }
- if (number === 0) {
- isNegative = false;
- }
+ // extract decimals digits
+ if (integerLen > 0) {
+ decimals = digits.splice(integerLen);
+ } else {
+ decimals = digits;
+ digits = [0];
+ }
+
+ // format the integer digits with grouping separators
+ var groups = [];
+ if (digits.length >= pattern.lgSize) {
+ groups.unshift(digits.splice(-pattern.lgSize).join(''));
+ }
+ while (digits.length > pattern.gSize) {
+ groups.unshift(digits.splice(-pattern.gSize).join(''));
+ }
+ if (digits.length) {
+ groups.unshift(digits.join(''));
+ }
+ formattedText = groups.join(groupSep);
+
+ // append the decimal digits
+ if (decimals.length) {
+ formattedText += decimalSep + decimals.join('');
+ }
- parts.push(isNegative ? pattern.negPre : pattern.posPre,
- formatedText,
- isNegative ? pattern.negSuf : pattern.posSuf);
- return parts.join('');
+ if (exponent) {
+ formattedText += 'e+' + exponent;
+ }
+ }
+ if (number < 0 && !isZero) {
+ return pattern.negPre + formattedText + pattern.negSuf;
+ } else {
+ return pattern.posPre + formattedText + pattern.posSuf;
+ }
}
-function padNumber(num, digits, trim) {
+function padNumber(num, digits, trim, negWrap) {
var neg = '';
- if (num < 0) {
- neg = '-';
- num = -num;
+ if (num < 0 || (negWrap && num <= 0)) {
+ if (negWrap) {
+ num = -num + 1;
+ } else {
+ num = -num;
+ neg = '-';
+ }
}
num = '' + num;
- while (num.length < digits) num = '0' + num;
+ while (num.length < digits) num = ZERO_CHAR + num;
if (trim) {
num = num.substr(num.length - digits);
}
@@ -23057,7 +24856,7 @@ function padNumber(num, digits, trim) {
}
-function dateGetter(name, size, offset, trim) {
+function dateGetter(name, size, offset, trim, negWrap) {
offset = offset || 0;
return function(date) {
var value = date['get' + name]();
@@ -23065,14 +24864,15 @@ function dateGetter(name, size, offset, trim) {
value += offset;
}
if (value === 0 && offset == -12) value = 12;
- return padNumber(value, size, trim);
+ return padNumber(value, size, trim, negWrap);
};
}
-function dateStrGetter(name, shortForm) {
+function dateStrGetter(name, shortForm, standAlone) {
return function(date, formats) {
var value = date['get' + name]();
- var get = uppercase(shortForm ? ('SHORT' + name) : name);
+ var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
+ var get = uppercase(propPrefix + name);
return formats[get][value];
};
@@ -23127,13 +24927,14 @@ function longEraGetter(date, formats) {
}
var DATE_FORMATS = {
- yyyy: dateGetter('FullYear', 4),
- yy: dateGetter('FullYear', 2, 0, true),
- y: dateGetter('FullYear', 1),
+ yyyy: dateGetter('FullYear', 4, 0, false, true),
+ yy: dateGetter('FullYear', 2, 0, true, true),
+ y: dateGetter('FullYear', 1, 0, false, true),
MMMM: dateStrGetter('Month'),
MMM: dateStrGetter('Month', true),
MM: dateGetter('Month', 2, 1),
M: dateGetter('Month', 1, 1),
+ LLLL: dateStrGetter('Month', false, true),
dd: dateGetter('Date', 2),
d: dateGetter('Date', 1),
HH: dateGetter('Hours', 2),
@@ -23159,7 +24960,7 @@ var DATE_FORMATS = {
GGGG: longEraGetter
};
-var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
+var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
NUMBER_STRING = /^\-?\d+$/;
/**
@@ -23179,6 +24980,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|
* * `'MMM'`: Month in year (Jan-Dec)
* * `'MM'`: Month in year, padded (01-12)
* * `'M'`: Month in year (1-12)
+ * * `'LLLL'`: Stand-alone month in year (January-December)
* * `'dd'`: Day in month, padded (01-31)
* * `'d'`: Day in month (1-31)
* * `'EEEE'`: Day in Week,(Sunday-Saturday)
@@ -23318,13 +25120,13 @@ function dateFilter($locale) {
var dateTimezoneOffset = date.getTimezoneOffset();
if (timezone) {
- dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
+ dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
date = convertTimezoneToLocal(date, timezone, true);
}
forEach(parts, function(value) {
fn = DATE_FORMATS[value];
text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
- : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
+ : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
});
return text;
@@ -23505,7 +25307,7 @@ function limitToFilter() {
if (!isArray(input) && !isString(input)) return input;
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
- begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
+ begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
if (limit >= 0) {
return input.slice(begin, begin + limit);
@@ -23528,8 +25330,9 @@ function limitToFilter() {
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
* as expected, make sure they are actually being saved as numbers and not strings.
+ * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
*
- * @param {Array} array The array to sort.
+ * @param {Array} array The array (or array-like object) to sort.
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
* used by the comparator to determine the order of elements.
*
@@ -23560,17 +25363,6 @@ function limitToFilter() {
* `reverse` is not set, which means it defaults to `false`.
-
@@ -23586,6 +25378,17 @@ function limitToFilter() {
+
+ angular.module('orderByExample', [])
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.friends =
+ [{name:'John', phone:'555-1212', age:10},
+ {name:'Mary', phone:'555-9876', age:19},
+ {name:'Mike', phone:'555-4321', age:21},
+ {name:'Adam', phone:'555-5678', age:35},
+ {name:'Julie', phone:'555-8765', age:29}];
+ }]);
+
*
* The predicate and reverse parameters can be controlled dynamically through scope properties,
@@ -23593,49 +25396,24 @@ function limitToFilter() {
* @example
-
-
@@ -23684,19 +25496,33 @@ function limitToFilter() {
{ name: 'Adam', phone: '555-5678', age: 35 },
{ name: 'Julie', phone: '555-8765', age: 29 }
];
- $scope.order = function(predicate, reverse) {
- $scope.friends = orderBy($scope.friends, predicate, reverse);
+ $scope.order = function(predicate) {
+ $scope.predicate = predicate;
+ $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
+ $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
};
- $scope.order('-age',false);
+ $scope.order('age', true);
}]);
+
+
+ .sortorder:after {
+ content: '\25b2';
+ }
+ .sortorder.reverse:after {
+ content: '\25bc';
+ }
+
*/
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
return function(array, sortPredicate, reverseOrder) {
- if (!(isArrayLike(array))) return array;
+ if (array == null) return array;
+ if (!isArrayLike(array)) {
+ throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
+ }
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
@@ -24019,20 +25845,7 @@ var htmlAnchorDirective = valueFn({
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
*
* A special directive is necessary because we cannot use interpolation inside the `disabled`
- * attribute. The following example would make the button enabled on Chrome/Firefox
- * but not on older IEs:
- *
- * ```html
- *
- *
- *
- *
- * ```
- *
- * This is because the HTML specification does not require browsers to preserve the values of
- * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
- * If we put an Angular interpolation expression into such an attribute then the
- * binding information would be lost when the browser removes the attribute.
+ * attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
* @example
@@ -24067,15 +25880,9 @@ var htmlAnchorDirective = valueFn({
* Note that this directive should not be used together with {@link ngModel `ngModel`},
* as this can lead to unexpected behavior.
*
- * ### Why do we need `ngChecked`?
+ * A special directive is necessary because we cannot use interpolation inside the `checked`
+ * attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
- * The HTML specification does not require browsers to preserve the values of boolean attributes
- * such as checked. (Their presence means true and their absence means false.)
- * If we put an Angular interpolation expression into such an attribute then the
- * binding information would be lost when the browser removes the attribute.
- * The `ngChecked` directive solves this problem for the `checked` attribute.
- * This complementary directive is not removed by the browser and so provides
- * a permanent reliable place to store the binding information.
* @example
@@ -24104,13 +25911,12 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- * The HTML specification does not require browsers to preserve the values of boolean attributes
- * such as readonly. (Their presence means true and their absence means false.)
- * If we put an Angular interpolation expression into such an attribute then the
- * binding information would be lost when the browser removes the attribute.
- * The `ngReadonly` directive solves this problem for the `readonly` attribute.
- * This complementary directive is not removed by the browser and so provides
- * a permanent reliable place to store the binding information.
+ *
+ * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
+ *
+ * A special directive is necessary because we cannot use interpolation inside the `readOnly`
+ * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ *
* @example
@@ -24139,13 +25945,11 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- * The HTML specification does not require browsers to preserve the values of boolean attributes
- * such as selected. (Their presence means true and their absence means false.)
- * If we put an Angular interpolation expression into such an attribute then the
- * binding information would be lost when the browser removes the attribute.
- * The `ngSelected` directive solves this problem for the `selected` attribute.
- * This complementary directive is not removed by the browser and so provides
- * a permanent reliable place to store the binding information.
+ *
+ * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
+ *
+ * A special directive is necessary because we cannot use interpolation inside the `selected`
+ * attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
* @example
@@ -24177,13 +25981,12 @@ var htmlAnchorDirective = valueFn({
* @priority 100
*
* @description
- * The HTML specification does not require browsers to preserve the values of boolean attributes
- * such as open. (Their presence means true and their absence means false.)
- * If we put an Angular interpolation expression into such an attribute then the
- * binding information would be lost when the browser removes the attribute.
- * The `ngOpen` directive solves this problem for the `open` attribute.
- * This complementary directive is not removed by the browser and so provides
- * a permanent reliable place to store the binding information.
+ *
+ * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
+ *
+ * A special directive is necessary because we cannot use interpolation inside the `open`
+ * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ *
* @example
@@ -24429,7 +26232,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
*
* However, if the method is used programmatically, for example by adding dynamically created controls,
* or controls that have been previously removed without destroying their corresponding DOM element,
- * it's the developers responsiblity to make sure the current state propagates to the parent form.
+ * it's the developers responsibility to make sure the current state propagates to the parent form.
*
* For example, if an input control is added that is already `$dirty` and has `$error` properties,
* calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
@@ -24639,13 +26442,9 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
*
* In Angular, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
- $scope.user = { name: 'say', data: '' };
+ $scope.user = { name: 'John', data: '' };
$scope.cancel = function(e) {
if (e.keyCode == 27) {
@@ -30042,20 +31959,20 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
var other = element(by.model('user.data'));
it('should allow custom events', function() {
- input.sendKeys(' hello');
+ input.sendKeys(' Doe');
input.click();
- expect(model.getText()).toEqual('say');
+ expect(model.getText()).toEqual('John');
other.click();
- expect(model.getText()).toEqual('say hello');
+ expect(model.getText()).toEqual('John Doe');
});
it('should $rollbackViewValue when model changes', function() {
- input.sendKeys(' hello');
- expect(input.getAttribute('value')).toEqual('say hello');
+ input.sendKeys(' Doe');
+ expect(input.getAttribute('value')).toEqual('John Doe');
input.sendKeys(protractor.Key.ESCAPE);
- expect(input.getAttribute('value')).toEqual('say');
+ expect(input.getAttribute('value')).toEqual('John');
other.click();
- expect(model.getText()).toEqual('say');
+ expect(model.getText()).toEqual('John');
});
@@ -30081,7 +31998,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
- $scope.user = { name: 'say' };
+ $scope.user = { name: 'Igor' };
}]);
@@ -30314,19 +32231,27 @@ var ngOptionsMinErr = minErr('ngOptions');
*
* ## Complex Models (objects or collections)
*
- * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
- * binding any input directive to a model that is an object or a collection.
+ * By default, `ngModel` watches the model by reference, not value. This is important to know when
+ * binding the select to a model that is an object or a collection.
*
- * Since this is a common situation for `ngOptions` the directive additionally watches the model using
- * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
- * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
- * object/collection has not changed identity but only a property on the object or an item in the collection
- * changes.
+ * One issue occurs if you want to preselect an option. For example, if you set
+ * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
+ * because the objects are not identical. So by default, you should always reference the item in your collection
+ * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
*
- * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
- * if the model is an array). This means that changing a property deeper inside the object/collection that the
- * first level will not trigger a re-rendering.
+ * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
+ * of the item not by reference, but by the result of the `track by` expression. For example, if your
+ * collection items have an id property, you would `track by item.id`.
+ *
+ * A different issue with objects or collections is that ngModel won't detect if an object property or
+ * a collection item changes. For that reason, `ngOptions` additionally watches the model using
+ * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
+ * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
+ * has not changed identity, but only a property on the object or an item in the collection changes.
*
+ * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
+ * if the model is an array). This means that changing a property deeper than the first level inside the
+ * object/collection will not trigger a re-rendering.
*
* ## `select` **`as`**
*
@@ -30339,17 +32264,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* ### `select` **`as`** and **`track by`**
*
*
- * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
+ * Be careful when using `select` **`as`** and **`track by`** in the same expression.
*
*
- * Consider the following example:
- *
- * ```html
- *
- * ```
+ * Given this array of items on the $scope:
*
* ```js
- * $scope.values = [{
+ * $scope.items = [{
* id: 1,
* label: 'aLabel',
* subItem: { name: 'aSubItem' }
@@ -30358,20 +32279,33 @@ var ngOptionsMinErr = minErr('ngOptions');
* label: 'bLabel',
* subItem: { name: 'bSubItem' }
* }];
+ * ```
+ *
+ * This will work:
*
- * $scope.selected = { name: 'aSubItem' };
+ * ```html
+ *
+ * ```
+ * ```js
+ * $scope.selected = $scope.items[0];
* ```
*
- * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
- * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
- * following:
+ * but this will not work:
+ *
+ * ```html
+ *
+ * ```
+ * ```js
+ * $scope.selected = $scope.items[0].subItem;
+ * ```
*
- * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
- * 2. Apply **`track by`** to the already selected value in `ngModel`.
- * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
- * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
- * a wrong object, the selected element can't be found, `
* ```
*
- * You may use arbitrary expressions in `track by`, including references to custom functions
+ * You may also use arbitrary expressions in `track by`, including references to custom functions
* on the scope:
* ```html
*
* ```
*
- * If you are working with objects that have an identifier property, you can track
+ *
+ * If you are working with objects that have an identifier property, you should track
* by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
* will not have to rebuild the DOM elements for items it has already rendered, even if the
- * JavaScript objects in the collection have been substituted for new ones:
+ * JavaScript objects in the collection have been substituted for new ones. For large collections,
+ * this significantly improves rendering performance. If you don't have a unique identifier,
+ * `track by $index` can also provide a performance boost.
+ *
* ```html
*
* {{model.name}}
@@ -31400,11 +33316,13 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
*
* @animations
- * **.enter** - when a new item is added to the list or when an item is revealed after a filter
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
+ * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
+ * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
*
- * **.leave** - when an item is removed from the list or when an item is filtered out
- *
- * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
+ * See the example below for defining CSS animations with ngRepeat.
*
* @element ANY
* @scope
@@ -31458,22 +33376,11 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
*
* @example
- * This example initializes the scope to a list of names and
- * then uses `ngRepeat` to display every person:
-
+ * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
+ * results by name. New (entering) and removed (leaving) items are animated.
+
-
+
+ angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
+ $scope.friends = [
+ {name:'John', age:25, gender:'boy'},
+ {name:'Jessie', age:30, gender:'girl'},
+ {name:'Johanna', age:28, gender:'girl'},
+ {name:'Joy', age:15, gender:'girl'},
+ {name:'Mary', age:28, gender:'girl'},
+ {name:'Peter', age:95, gender:'boy'},
+ {name:'Sebastian', age:50, gender:'boy'},
+ {name:'Erika', age:27, gender:'girl'},
+ {name:'Patrick', age:40, gender:'boy'},
+ {name:'Samantha', age:60, gender:'girl'}
+ ];
+ });
+
.example-animate-container {
background:white;
@@ -31496,7 +33419,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
}
.animate-repeat {
- line-height:40px;
+ line-height:30px;
list-style:none;
box-sizing:border-box;
}
@@ -31518,7 +33441,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
.animate-repeat.ng-move.ng-move-active,
.animate-repeat.ng-enter.ng-enter-active {
opacity:1;
- max-height:40px;
+ max-height:30px;
}
@@ -31545,7 +33468,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
*/
-var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
+var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
var NG_REMOVED = '$$NG_REMOVED';
var ngRepeatMinErr = minErr('ngRepeat');
@@ -31580,7 +33503,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
$$tlb: true,
compile: function ngRepeatCompile($element, $attr) {
var expression = $attr.ngRepeat;
- var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
+ var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
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*$/);
@@ -31744,7 +33667,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
if (getBlockStart(block) != nextNode) {
// existing item which got moved
- $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
+ $animate.move(getBlockNodes(block.clone), null, previousNode);
}
previousNode = getBlockEnd(block);
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
@@ -31756,8 +33679,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var endNode = ngRepeatEndComment.cloneNode(false);
clone[clone.length++] = endNode;
- // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
- $animate.enter(clone, null, jqLite(previousNode));
+ $animate.enter(clone, null, previousNode);
previousNode = endNode;
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
@@ -31860,12 +33782,14 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
- * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
*
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
@@ -32024,12 +33948,15 @@ var ngShowDirective = ['$animate', function($animate) {
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
- * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
+ *
*
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
@@ -32191,8 +34118,10 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
*
* @animations
- * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
- * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
+ * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
*
* @usage
*
@@ -32291,7 +34220,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
*/
-var ngSwitchDirective = ['$animate', function($animate) {
+var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
return {
require: 'ngSwitch',
@@ -32332,7 +34261,7 @@ var ngSwitchDirective = ['$animate', function($animate) {
selectedTransclude.transclude(function(caseElement, selectedScope) {
selectedScopes.push(selectedScope);
var anchor = selectedTransclude.element;
- caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
+ caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
var block = { clone: caseElement };
selectedElements.push(block);
@@ -32375,67 +34304,186 @@ var ngSwitchDefaultDirective = ngDirective({
* @description
* Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
*
- * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
+ * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
+ * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
+ *
+ * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
+ * content of this element will be removed before the transcluded content is inserted.
+ * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
+ * that no transcluded content is provided.
*
* @element ANY
*
+ * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
+ * or its value is the same as the name of the attribute then the default slot is used.
+ *
* @example
-
-
-
-
-
-
- {{text}}
-
-
-
- it('should have transcluded', function() {
- var titleElement = element(by.model('title'));
- titleElement.clear();
- titleElement.sendKeys('TITLE');
- var textElement = element(by.model('text'));
- textElement.clear();
- textElement.sendKeys('TEXT');
- expect(element(by.binding('title')).getText()).toEqual('TITLE');
- expect(element(by.binding('text')).getText()).toEqual('TEXT');
- });
-
-
+ * ### Basic transclusion
+ * This example demonstrates basic transclusion of content into a component directive.
+ *
+ *
+ *
+ *
+ *
+ *
+ * {{text}}
+ *
+ *
+ *
+ * it('should have transcluded', function() {
+ * var titleElement = element(by.model('title'));
+ * titleElement.clear();
+ * titleElement.sendKeys('TITLE');
+ * var textElement = element(by.model('text'));
+ * textElement.clear();
+ * textElement.sendKeys('TEXT');
+ * expect(element(by.binding('title')).getText()).toEqual('TITLE');
+ * expect(element(by.binding('text')).getText()).toEqual('TEXT');
+ * });
+ *
+ *
+ *
+ * @example
+ * ### Transclude fallback content
+ * This example shows how to use `NgTransclude` with fallback content, that
+ * is displayed if no transcluded content is provided.
*
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Button2
+ *
+ *
+ *
+ * it('should have different transclude element content', function() {
+ * expect(element(by.id('fallback')).getText()).toBe('Button1');
+ * expect(element(by.id('modified')).getText()).toBe('Button2');
+ * });
+ *
+ *
+ *
+ * @example
+ * ### Multi-slot transclusion
+ * This example demonstrates using multi-slot transclusion in a component directive.
+ *
+ *
+ *
+ *