/**
- * @license AngularJS v1.4.10
+ * @license AngularJS v1.4.8
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
return match;
});
- message += '\nhttp://errors.angularjs.org/1.4.10/' +
+ message += '\nhttp://errors.angularjs.org/1.4.8/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
// NodeList objects (with `item` method) and
// other objects with suitable length characteristics are array-like
return isNumber(length) &&
- (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
-
+ (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
}
/**
*
* Unlike ES262's
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
- * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
+ * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
* return the value provided.
*
```js
* @returns {function(*, string)}
*/
function reverseParams(iteratorFn) {
- return function(value, key) {iteratorFn(key, value);};
+ return function(value, key) { iteratorFn(key, value); };
}
/**
* @returns {object} in the form of {key1:true, key2:true, ...}
*/
function makeMap(str) {
- var obj = {}, items = str.split(','), i;
+ var obj = {}, items = str.split(","), i;
for (i = 0; i < items.length; i++) {
obj[items[i]] = true;
}
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
- } else if (isBlob(source)) {
- destination = new source.constructor([source], {type: source.type});
} else if (isFunction(source.cloneNode)) {
destination = source.cloneNode(true);
} else {
* @returns {string|undefined} JSON-ified string representing `obj`.
*/
function toJson(obj, pretty) {
- if (isUndefined(obj)) return undefined;
+ if (typeof obj === 'undefined') return undefined;
if (!isNumber(pretty)) {
pretty = pretty ? 2 : null;
}
}
-var ALL_COLONS = /:/g;
function timezoneToOffset(timezone, fallback) {
- // IE/Edge do not "understand" colon (`:`) in timezone
- timezone = timezone.replace(ALL_COLONS, '');
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
}
function convertTimezoneToLocal(date, timezone, reverse) {
reverse = reverse ? -1 : 1;
- var dateTimezoneOffset = date.getTimezoneOffset();
- var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
- return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
+ var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
+ return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
}
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
- replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
+ replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
} catch (e) {
return lowercase(elemHtml);
}
* unspecified then the module is being retrieved for further configuration.
* @param {Function=} configFn Optional configuration function for the module. Same as
* {@link angular.Module#config Module#config()}.
- * @returns {angular.Module} new module with the {@link angular.Module} api.
+ * @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
$AnchorScrollProvider,
$AnimateProvider,
$CoreAnimateCssProvider,
- $$CoreAnimateJsProvider,
$$CoreAnimateQueueProvider,
- $$AnimateRunnerFactoryProvider,
- $$AnimateAsyncRunFactoryProvider,
+ $$CoreAnimateRunnerProvider,
$BrowserProvider,
$CacheFactoryProvider,
$ControllerProvider,
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.4.10', // all of these placeholder strings will be replaced by grunt's
+ full: '1.4.8', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 4,
- dot: 10,
- codeName: 'benignant-oscillation'
+ dot: 8,
+ codeName: 'ice-manipulation'
};
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
$animateCss: $CoreAnimateCssProvider,
- $$animateJs: $$CoreAnimateJsProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
- $$AnimateRunner: $$AnimateRunnerFactoryProvider,
- $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
+ $$AnimateRunner: $$CoreAnimateRunnerProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
*
* If jQuery is available, `angular.element` is an alias for the
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
- * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
- *
- * jqLite is a tiny, API-compatible subset of jQuery that allows
- * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
- * commonly needed functionality with the goal of having a very small footprint.
+ * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
*
- * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
- * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
- * specific version of jQuery if multiple versions exist on the page.
+ * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
+ * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
+ * commonly needed functionality with the goal of having a very small footprint.</div>
*
- * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
- * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
+ * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
*
- * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
- * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
- * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
+ * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
+ * jqLite; they are never raw DOM references.</div>
*
* ## Angular's jqLite
* jqLite provides only the following jQuery methods:
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
- * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
- * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
+ * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
* - [`data()`](http://api.jquery.com/data/)
* - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
return [];
}
-function jqLiteWrapNode(node, wrapper) {
- var parent = node.parentNode;
-
- if (parent) {
- parent.replaceChild(wrapper, node);
- }
-
- wrapper.appendChild(node);
-}
-
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
var jqLiteContains = Node.prototype.contains || function(arg) {
},
wrap: function(element, wrapNode) {
- jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
+ wrapNode = jqLite(wrapNode).eq(0).clone()[0];
+ var parent = element.parentNode;
+ if (parent) {
+ parent.replaceChild(wrapNode, element);
+ }
+ wrapNode.appendChild(element);
},
remove: jqLiteRemove,
*
* Register a **service constructor**, which will be invoked with `new` to create the service
* instance.
- * This is short for registering a service where its provider's `$get` property is a factory
- * function that returns an instance instantiated by the injector from the service constructor
- * function.
- *
- * Internally it looks a bit like this:
- *
- * ```
- * {
- * $get: function() {
- * return $injector.instantiate(constructor);
- * }
- * }
- * ```
- *
+ * This is short for registering a service where its provider's `$get` property is the service
+ * constructor function that will be used to instantiate the service instance.
*
* You should use {@link auto.$provide#service $provide.service(class)} if you define your service
* as a type/class.
* @description
*
* Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
- * number, an array, an object or a function. This is short for registering a service where its
+ * number, an array, an object or a function. This is short for registering a service where its
* provider's `$get` property is a factory function that takes no arguments and returns the **value
- * service**. That also means it is not possible to inject other services into a value service.
+ * service**.
*
* Value services are similar to constant services, except that they cannot be injected into a
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
- * an Angular {@link auto.$provide#decorator decorator}.
+ * an Angular
+ * {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
* @name $provide#constant
* @description
*
- * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
- * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
- * possible to inject other services into a constant.
- *
- * But unlike {@link auto.$provide#value value}, a constant can be
+ * Register a **constant service**, such as a string, a number, an array, an object or a function,
+ * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
* be overridden by an Angular {@link auto.$provide#decorator decorator}.
*
* @description
*
* Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
- * intercepts the creation of a service, allowing it to override or modify the behavior of the
+ * intercepts the creation of a service, allowing it to override or modify the behaviour of the
* service. The object returned by the decorator may be the original service, or a new service
* object which replaces or wraps and delegates to the original service.
*
: {};
}
-var $$CoreAnimateJsProvider = function() {
- this.$get = function() {};
+var $$CoreAnimateRunnerProvider = function() {
+ this.$get = ['$q', '$$rAF', function($q, $$rAF) {
+ function AnimateRunner() {}
+ AnimateRunner.all = noop;
+ AnimateRunner.chain = noop;
+ AnimateRunner.prototype = {
+ end: noop,
+ cancel: noop,
+ resume: noop,
+ pause: noop,
+ complete: noop,
+ then: function(pass, fail) {
+ return $q(function(resolve) {
+ $$rAF(function() {
+ resolve();
+ });
+ }).then(pass, fail);
+ }
+ };
+ return AnimateRunner;
+ }];
};
// this is prefixed with Core since it conflicts with
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
}
- var runner = new $$AnimateRunner(); // jshint ignore:line
-
- // since there are no animations to run the runner needs to be
- // notified that the animation call is complete.
- runner.complete();
- return runner;
+ return new $$AnimateRunner(); // jshint ignore:line
}
};
* @kind function
*
* @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
- * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
- * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
- * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
- * style in `to`, the style in `from` is applied immediately, and no animation is run.
- * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
- * method (or as part of the `options` parameter):
- *
- * ```js
- * ngModule.animation('.my-inline-animation', function() {
- * return {
- * animate : function(element, from, to, done, options) {
- * //animation
- * done();
- * }
- * }
- * });
- * ```
+ * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
+ * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
+ * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
+ * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
*
* @param {DOMElement} element the element which the CSS styles will be applied to
* @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
}];
}];
-var $$AnimateAsyncRunFactoryProvider = function() {
- this.$get = ['$$rAF', function($$rAF) {
- var waitQueue = [];
-
- function waitForTick(fn) {
- waitQueue.push(fn);
- if (waitQueue.length > 1) return;
- $$rAF(function() {
- for (var i = 0; i < waitQueue.length; i++) {
- waitQueue[i]();
- }
- waitQueue = [];
- });
- }
-
- return function() {
- var passed = false;
- waitForTick(function() {
- passed = true;
- });
- return function(callback) {
- passed ? callback() : waitForTick(callback);
- };
- };
- }];
-};
-
-var $$AnimateRunnerFactoryProvider = function() {
- this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
- function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
-
- var INITIAL_STATE = 0;
- var DONE_PENDING_STATE = 1;
- var DONE_COMPLETE_STATE = 2;
-
- AnimateRunner.chain = function(chain, callback) {
- var index = 0;
-
- next();
- function next() {
- if (index === chain.length) {
- callback(true);
- return;
- }
-
- chain[index](function(response) {
- if (response === false) {
- callback(false);
- return;
- }
- index++;
- next();
- });
- }
- };
-
- AnimateRunner.all = function(runners, callback) {
- var count = 0;
- var status = true;
- forEach(runners, function(runner) {
- runner.done(onProgress);
- });
-
- function onProgress(response) {
- status = status && response;
- if (++count === runners.length) {
- callback(status);
- }
- }
- };
-
- function AnimateRunner(host) {
- this.setHost(host);
-
- var rafTick = $$animateAsyncRun();
- var timeoutTick = function(fn) {
- $timeout(fn, 0, false);
- };
-
- this._doneCallbacks = [];
- this._tick = function(fn) {
- var doc = $document[0];
-
- // the document may not be ready or attached
- // to the module for some internal tests
- if (doc && doc.hidden) {
- timeoutTick(fn);
- } else {
- rafTick(fn);
- }
- };
- this._state = 0;
- }
-
- AnimateRunner.prototype = {
- setHost: function(host) {
- this.host = host || {};
- },
-
- done: function(fn) {
- if (this._state === DONE_COMPLETE_STATE) {
- fn();
- } else {
- this._doneCallbacks.push(fn);
- }
- },
-
- progress: noop,
-
- getPromise: function() {
- if (!this.promise) {
- var self = this;
- this.promise = $q(function(resolve, reject) {
- self.done(function(status) {
- status === false ? reject() : resolve();
- });
- });
- }
- return this.promise;
- },
-
- then: function(resolveHandler, rejectHandler) {
- return this.getPromise().then(resolveHandler, rejectHandler);
- },
-
- 'catch': function(handler) {
- return this.getPromise()['catch'](handler);
- },
-
- 'finally': function(handler) {
- return this.getPromise()['finally'](handler);
- },
-
- pause: function() {
- if (this.host.pause) {
- this.host.pause();
- }
- },
-
- resume: function() {
- if (this.host.resume) {
- this.host.resume();
- }
- },
-
- end: function() {
- if (this.host.end) {
- this.host.end();
- }
- this._resolve(true);
- },
-
- cancel: function() {
- if (this.host.cancel) {
- this.host.cancel();
- }
- this._resolve(false);
- },
-
- complete: function(response) {
- var self = this;
- if (self._state === INITIAL_STATE) {
- self._state = DONE_PENDING_STATE;
- self._tick(function() {
- self._resolve(response);
- });
- }
- },
-
- _resolve: function(response) {
- if (this._state !== DONE_COMPLETE_STATE) {
- forEach(this._doneCallbacks, function(fn) {
- fn(response);
- });
- this._doneCallbacks.length = 0;
- this._state = DONE_COMPLETE_STATE;
- }
- }
- };
-
- return AnimateRunner;
- }];
-};
-
/**
* @ngdoc service
* @name $animateCss
* Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
*/
var $CoreAnimateCssProvider = function() {
- this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
+ this.$get = ['$$rAF', '$q', function($$rAF, $q) {
- return function(element, initialOptions) {
- // all of the animation functions should create
- // a copy of the options data, however, if a
- // parent service has already created a copy then
- // we should stick to using that
- var options = initialOptions || {};
- if (!options.$$prepared) {
- options = copy(options);
+ var RAFPromise = function() {};
+ RAFPromise.prototype = {
+ done: function(cancel) {
+ this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
+ },
+ end: function() {
+ this.done();
+ },
+ cancel: function() {
+ this.done(true);
+ },
+ getPromise: function() {
+ if (!this.defer) {
+ this.defer = $q.defer();
+ }
+ return this.defer.promise;
+ },
+ then: function(f1,f2) {
+ return this.getPromise().then(f1,f2);
+ },
+ 'catch': function(f1) {
+ return this.getPromise()['catch'](f1);
+ },
+ 'finally': function(f1) {
+ return this.getPromise()['finally'](f1);
}
+ };
+ return function(element, options) {
// there is no point in applying the styles since
// there is no animation that goes on at all in
// this version of $animateCss.
options.from = null;
}
- /* jshint newcap: false*/
- var closed, runner = new $$AnimateRunner();
+ var closed, runner = new RAFPromise();
return {
start: run,
end: run
function run() {
$$rAF(function() {
- applyAnimationContents();
+ close();
if (!closed) {
- runner.complete();
+ runner.done();
}
closed = true;
});
return runner;
}
- function applyAnimationContents() {
+ function close() {
if (options.addClass) {
element.addClass(options.addClass);
options.addClass = null;
* is bound to the parent scope, via matching attributes on the directive's element:
*
* * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
- * always a string since DOM attributes are strings. If no `attr` name is specified then the
- * attribute name is assumed to be the same as the local name. Given `<my-component
- * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
- * the directive's scope property `localName` will reflect the interpolated value of `hello
- * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
- * scope. The `name` is read from the parent scope (not the directive's scope).
- *
- * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
- * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
- * If no `attr` name is specified then the attribute name is assumed to be the same as the local
- * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
- * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
- * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
- * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
- * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
- * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
- * will be thrown upon discovering changes to the local value, since it will be impossible to sync
- * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
- * method is used for tracking changes, and the equality check is based on object identity.
- * However, if an object literal or an array literal is passed as the binding expression, the
- * equality check is done by value (using the {@link angular.equals} function). It's also possible
- * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
- * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
- *
- * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
- * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
- * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
- * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
- * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
- * via an expression to the parent scope. This can be done by passing a map of local variable names
- * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
- * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
+ * always a string since DOM attributes are strings. If no `attr` name is specified then the
+ * attribute name is assumed to be the same as the local name.
+ * Given `<widget my-attr="hello {{name}}">` and widget definition
+ * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
+ * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
+ * `localName` property on the widget scope. The `name` is read from the parent scope (not
+ * component scope).
+ *
+ * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
+ * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
+ * name is specified then the attribute name is assumed to be the same as the local name.
+ * Given `<widget my-attr="parentModel">` and widget definition of
+ * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
+ * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
+ * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
+ * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
+ * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
+ * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
+ * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
+ *
+ * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
+ * If no `attr` name is specified then the attribute name is assumed to be the same as the
+ * local name. Given `<widget my-attr="count = count + value">` and widget definition of
+ * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
+ * a function wrapper for the `count = count + value` expression. Often it's desirable to
+ * pass data from the isolated scope via an expression to the parent scope, this can be
+ * done by passing a map of local variable names and values into the expression wrapper fn.
+ * For example, if the expression is `increment(amount)` then we can specify the amount value
+ * by calling the `localFn` as `localFn({amount: 22})`.
*
* In general it's possible to apply more than one directive to one element, but there might be limitations
* depending on the type of scope required by the directives. The following points will help explain these limitations.
*
*
* #### `bindToController`
- * This property is used to bind scope properties directly to the controller. It can be either
- * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
- * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
- * definition: `controller: 'myCtrl as myAlias'`.
- *
- * When an isolate scope is used for a directive (see above), `bindToController: true` will
+ * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
* is instantiated, the initial values of the isolate scope bindings are already available.
*
- * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
- * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
- * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
- * scope (useful for component directives).
- *
- * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
- *
- *
* #### `controller`
* Controller constructor function. The controller is instantiated before the
* pre-linking phase and can be accessed by other directives (see
* <div class="alert alert-warning">
* **Note:** The compile function cannot handle directives that recursively use themselves in their
- * own templates or compile functions. Compiling these directives results in an infinite loop and
+ * own templates or compile functions. Compiling these directives results in an infinite loop and a
* stack overflow errors.
*
* This can be avoided by manually using $compile in the postLink function to imperatively compile
*
* The `$parent` scope hierarchy will look like this:
*
- ```
- - $rootScope
- - isolate
- - transclusion
- ```
+ * ```
+ * - $rootScope
+ * - isolate
+ * - transclusion
+ * ```
*
* but the scopes will inherit prototypically from different scopes to their `$parent`.
*
- ```
- - $rootScope
- - transclusion
- - isolate
- ```
+ * ```
+ * - $rootScope
+ * - transclusion
+ * - isolate
+ * ```
*
*
* ### Attributes
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses.
*
- * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
- * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
- * to the attributes.
+ * accessing *Normalized attribute names:*
+ * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
+ * the attributes object allows for normalized access to
+ * the attributes.
*
* * *Directive inter-communication:* All directives share the same instance of the attributes
* object which allows the directives to use the attributes object as inter directive
* directives; if given, it will be passed through to the link functions of
* directives found in `element` during compilation.
* * `transcludeControllers` - an object hash with keys that map controller names
- * to a hash with the key `instance`, which maps to the controller instance;
- * if given, it will make the controllers available to directives on the compileNode:
- * ```
- * {
- * parent: {
- * instance: parentControllerInstance
- * }
- * }
- * ```
+ * to controller instances; if given, it will make the controllers
+ * available to directives.
* * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
* the cloned elements; only needed for transcludes that are allowed to contain non html
* elements (e.g. SVG elements). See also the directive.controller property.
// The assumption is that future DOM event attribute names will begin with
// 'on' and be composed of only English letters.
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
- var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
var bindings = {};
forEach(scope, function(definition, scopeName) {
- if (definition in bindingCache) {
- bindings[scopeName] = bindingCache[definition];
- return;
- }
var match = definition.match(LOCAL_REGEXP);
if (!match) {
optional: match[3] === '?',
attrName: match[4] || scopeName
};
- if (match[4]) {
- bindingCache[definition] = bindings[scopeName];
- }
});
return bindings;
directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'EA';
+ var bindings = directive.$$bindings =
+ parseDirectiveBindings(directive, directive.name);
+ if (isObject(bindings.isolateScope)) {
+ directive.$$isolateBindings = bindings.isolateScope;
+ }
directive.$$moduleName = directiveFactory.$$moduleName;
directives.push(directive);
} catch (e) {
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
- '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
+ '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
- $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
+ $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
var Attributes = function(element, attributesToCopy) {
if (attributesToCopy) {
* @param {string} key Normalized key. (ie ngAttribute) .
* @param {function(interpolatedValue)} fn Function that will be called whenever
the interpolated value of the attribute changes.
- * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
- * guide} for more info.
+ * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
* @returns {function()} Returns a deregistration function for this observer.
*/
$observe: function(key, fn) {
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
- denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
+ denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
: function denormalizeTemplate(template) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
// modify it.
$compileNodes = jqLite($compileNodes);
}
-
- var NOT_EMPTY = /\S+/;
-
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
- for (var i = 0, len = $compileNodes.length; i < len; i++) {
- var domNode = $compileNodes[i];
-
- if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
- jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
+ forEach($compileNodes, function(node, index) {
+ if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
+ $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
}
- }
-
+ });
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
- // For directives with element transclusion the element is a comment.
- // In this case .data will not attach any data.
+ // For directives with element transclusion the element is a comment,
+ // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
+ // clean up (http://bugs.jquery.com/ticket/8335).
// Instead, we save the controllers for the element in a local hash and attach to .data
// later, once we have the actual element.
elementControllers[directive.name] = controllerInstance;
- $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
+ if (!hasElementTranscludeDirective) {
+ $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
+ }
}
return elementControllers;
}
if (startAttrName) {
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
}
- if (!directive.$$bindings) {
- var bindings = directive.$$bindings =
- parseDirectiveBindings(directive, directive.name);
- if (isObject(bindings.isolateScope)) {
- directive.$$isolateBindings = bindings.isolateScope;
- }
- }
tDirectives.push(directive);
match = directive;
}
}
});
attrs.$$observers[attrName].$$scope = scope;
- lastValue = attrs[attrName];
- if (isString(lastValue)) {
+ if (isString(attrs[attrName])) {
// If the attribute has been provided then we trigger an interpolation to ensure
// the value is there for use in the link fn
- destination[scopeName] = $interpolate(lastValue)(scope);
- } else if (isBoolean(lastValue)) {
- // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
- // the value to boolean rather than a string, so we special case this situation
- destination[scopeName] = lastValue;
+ destination[scopeName] = $interpolate(attrs[attrName])(scope);
}
break;
// reset the change, or we will throw this exception on every $digest
lastValue = destination[scopeName] = parentGet(scope);
throw $compileMinErr('nonassign',
- "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
- attrs[attrName], attrName, directive.name);
+ "Expression '{0}' used with directive '{1}' is non-assignable!",
+ attrs[attrName], directive.name);
};
lastValue = destination[scopeName] = parentGet(scope);
var parentValueWatch = function parentValueWatch(parentValue) {
var $controllerMinErr = minErr('$controller');
-var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
+var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
function identifierForController(controller, ident) {
if (ident && isString(ident)) return ident;
if (isString(controller)) {
*
* Object containing default values for all {@link ng.$http $http} requests.
*
- * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
- * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
- * by default. See {@link $http#caching $http Caching} for more information.
+ * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
+ * that will provide the cache for all requests who set their `cache` property to `true`.
+ * If you set the `defaults.cache = false` then only requests that specify their own custom
+ * cache object will be cached. See {@link $http#caching $http Caching} for more information.
*
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
* Defaults value is `'XSRF-TOKEN'`.
* the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
*
- * <div class="alert alert-warning">
- * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
- * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
- * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
- * function will be reflected on the scope and in any templates where the object is data-bound.
- * To prevent his, transform functions should have no side-effects.
- * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
- * </div>
- *
* ### Default Transformations
*
* The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
*
* ## Caching
*
- * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
- * set the config.cache value or the default cache value to TRUE or to a cache object (created
- * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
- * precedence over the default cache value.
- *
- * In order to:
- * * cache all responses - set the default cache value to TRUE or to a cache object
- * * cache a specific response - set config.cache value to TRUE or to a cache object
- *
- * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
- * then the default `$cacheFactory($http)` object is used.
+ * To enable caching, set the request configuration `cache` property to `true` (to use default
+ * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
+ * When the cache is enabled, `$http` stores the response from the server in the specified
+ * cache. The next time the same request is made, the response is served from the cache without
+ * sending a request to the server.
*
- * The default cache value can be set by updating the
- * {@link ng.$http#defaults `$http.defaults.cache`} property or the
- * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
+ * Note that even if the response is served from cache, delivery of the data is asynchronous in
+ * the same way that real requests are.
*
- * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
- * the relevant cache object. The next time the same request is made, the response is returned
- * from the cache without sending a request to the server.
+ * If there are multiple GET requests for the same URL that should be cached using the same
+ * cache, but the cache is not populated yet, only one request to the server will be made and
+ * the remaining requests will be fulfilled using the response from the first request.
*
- * Take note that:
- *
- * * Only GET and JSONP requests are cached.
- * * The cache key is the request URL including search parameters; headers are not considered.
- * * Cached responses are returned asynchronously, in the same way as responses from the server.
- * * If multiple identical requests are made using the same cache, which is not yet populated,
- * one request will be made to the server and remaining requests will return the same response.
- * * A cache-control header on the response does not affect if or how responses are cached.
+ * You can change the default cache to a new object (built with
+ * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
+ * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
+ * their `cache` property to `true` will now use this cache object.
*
+ * If you set the default cache to `false` then only requests that specify their own custom
+ * cache object will be cached.
*
* ## Interceptors
*
*
* ### Cross Site Request Forgery (XSRF) Protection
*
- * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
- * which the attacker can trick an authenticated user into unknowingly executing actions on your
- * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
- * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
- * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
- * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
- * The header will not be set for cross-domain requests.
+ * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
+ * an unauthorized site can gain your user's private data. Angular provides a mechanism
+ * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
+ * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
+ * JavaScript that runs on your domain could read the cookie, your server can be assured that
+ * the XHR came from JavaScript running on your domain. The header will not be set for
+ * cross-domain requests.
*
* To take advantage of this, your server needs to set a token in a JavaScript readable session
* cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
* transform function or an array of such functions. The transform function takes the http
* response body, headers and status and returns its transformed (typically deserialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request
- * Overriding the Default Transformations}
+ * Overriding the Default TransformationjqLiks}
* - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
* prepare the string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as function registered with the
* by registering it as a {@link auto.$provide#service service}.
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
- * - **cache** – `{boolean|Object}` – A boolean value or object created with
- * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
- * See {@link $http#caching $http Caching} for more information.
+ * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
+ * GET request, otherwise if a cache instance built with
+ * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
+ * caching.
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
* that should abort the request when resolved.
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
- if (!isString(requestConfig.url)) {
- throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
- }
-
var config = extend({
method: 'get',
transformRequest: defaults.transformRequest,
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
*
* @example
-<example name="custom-interpolation-markup" module="customInterpolationApp">
+<example module="customInterpolationApp">
<file name="index.html">
<script>
var customInterpolationApp = angular.module('customInterpolationApp', []);
this.label = "This binding is brought you by // interpolation symbols.";
});
</script>
-<div ng-controller="DemoController as demo">
+<div ng-app="App" ng-controller="DemoController as demo">
//demo.label//
</div>
</file>
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);
right = this.nextId();
left = {};
if (!isAssignable(ast.left)) {
- throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
+ throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
}
this.recurse(ast.left, undefined, left, function() {
self.if_(self.notNull(left.context), function() {
rhs = right(scope, locals, assign, inputs);
rhs = getStringValue(rhs);
ensureSafeMemberName(rhs, expression);
- if (create && create !== 1) {
- ensureSafeAssignContext(lhs);
- if (lhs && !(lhs[rhs])) {
- lhs[rhs] = {};
- }
+ if (create && create !== 1 && lhs && !(lhs[rhs])) {
+ lhs[rhs] = {};
}
value = lhs[rhs];
ensureSafeObject(value, expression);
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) {
- ensureSafeAssignContext(lhs);
- if (lhs && !(lhs[right])) {
- lhs[right] = {};
- }
+ if (create && create !== 1 && lhs && !(lhs[right])) {
+ lhs[right] = {};
}
var value = lhs != null ? lhs[right] : undefined;
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
}
};
+var getterFnCacheDefault = createMap();
+var getterFnCacheExpensive = createMap();
+
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
csp: noUnsafeEval,
expensiveChecks: true
};
- var runningChecksEnabled = false;
-
- $parse.$$runningExpensiveChecks = function() {
- return runningChecksEnabled;
- };
-
- return $parse;
- function $parse(exp, interceptorFn, expensiveChecks) {
+ return function $parse(exp, interceptorFn, expensiveChecks) {
var parsedExpression, oneTime, cacheKey;
- expensiveChecks = expensiveChecks || runningChecksEnabled;
-
switch (typeof exp) {
case 'string':
exp = exp.trim();
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
- if (expensiveChecks) {
- parsedExpression = expensiveChecksInterceptor(parsedExpression);
- }
cache[cacheKey] = parsedExpression;
}
return addInterceptor(parsedExpression, interceptorFn);
return addInterceptor(exp, interceptorFn);
default:
- 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;
- }
+ return noop;
}
- }
+ };
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
*
*/
$digest: function() {
- var watch, value, last, fn, get,
+ var watch, value, last,
watchers,
length,
dirty, ttl = TTL,
// 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) {
- get = watch.get;
- if ((value = get(current)) !== (last = watch.last) &&
+ if ((value = watch.get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value === 'number' && typeof last === 'number'
dirty = true;
lastDirtyWatch = watch;
watch.last = watch.eq ? copy(value, null) : value;
- fn = watch.fn;
- fn(value, ((last === initWatchVal) ? value : last), current);
+ watch.fn(value, ((last === initWatchVal) ? value : last), current);
if (ttl < 5) {
logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = [];
});
}
- asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
+ asyncQueue.push({scope: this, expression: expr, locals: locals});
},
$$postDigest: function(fn) {
$applyAsync: function(expr) {
var scope = this;
expr && applyAsyncQueue.push($applyAsyncExpression);
- expr = $parse(expr);
scheduleApplyAsync();
function $applyAsyncExpression() {
}
/**
- * @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.
*/
* @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.
*
- * <div class="alert alert-warning">
- * **Note:** an empty whitelist array will block all URLs!
- * </div>
+ * Note: **an empty whitelist array will block all URLs**!
*
* @return {Array} the currently set whitelist array.
*
* @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.
*
* @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 promise
- * will be resolved with the return value of the `fn` 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.
*
*/
function timeout(fn, delay, invokeApply) {
return (val === null) ? 'null' : typeof val;
}
-var MAX_DIGITS = 22;
-var DECIMAL_SEP = '.';
-var ZERO_CHAR = '0';
-
/**
* @ngdoc filter
* @name currency
* Formats a number as text.
*
* If the input is null or undefined, it will just be returned.
- * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
+ * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
* If the input is not a number an empty string is returned.
*
*
* @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 fractionSize and places a “,” after each third digit.
+ * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
*
* @example
<example module="numberFilterExample">
</file>
</example>
*/
+
+
numberFilter.$inject = ['$locale'];
function numberFilter($locale) {
var formats = $locale.NUMBER_FORMATS;
};
}
-/**
- * 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;
-
- // Decimal point?
- if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
- numStr = numStr.replace(DECIMAL_SEP, '');
- }
-
- // 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;
- }
-
- // Count the number of leading zeros.
- for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
-
- 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);
- }
- }
+var DECIMAL_SEP = '.';
+function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
+ if (isObject(number)) return '';
- // 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;
- }
+ var isNegative = number < 0;
+ number = Math.abs(number);
- return { d: digits, e: exponent, i: numberOfIntegerDigits };
-}
+ var isInfinity = number === Infinity;
+ if (!isInfinity && !isFinite(number)) return '';
-/**
- * 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 numStr = number + '',
+ formatedText = '',
+ hasExponent = false,
+ parts = [];
- // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
- fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
+ if (isInfinity) formatedText = '\u221e';
- // The index of the digit to where rounding is to occur
- var roundAt = fractionSize + parsedNumber.i;
- var digit = digits[roundAt];
-
- if (roundAt > 0) {
- digits.splice(roundAt);
+ if (!isInfinity && numStr.indexOf('e') !== -1) {
+ var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
+ if (match && match[2] == '-' && match[3] > fractionSize + 1) {
+ number = 0;
} else {
- // We rounded to zero so reset the parsedNumber
- parsedNumber.i = 1;
- digits.length = roundAt = fractionSize + 1;
- for (var i=0; i < roundAt; i++) digits[i] = 0;
+ formatedText = numStr;
+ hasExponent = true;
}
+ }
- if (digit >= 5) digits[roundAt - 1]++;
-
- // Pad out with zeros to get the required fraction length
- for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
-
+ if (!isInfinity && !hasExponent) {
+ var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
- // 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++;
+ // determine fractionSize if it is not specified
+ if (isUndefined(fractionSize)) {
+ fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
}
-}
-
-/**
- * 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 '';
-
- var isInfinity = !isFinite(number);
- var isZero = false;
- var numStr = Math.abs(number) + '',
- formattedText = '',
- parsedNumber;
-
- if (isInfinity) {
- formattedText = '\u221e';
- } else {
- parsedNumber = parse(numStr);
- roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
+ // 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);
- var digits = parsedNumber.d;
- var integerLen = parsedNumber.i;
- var exponent = parsedNumber.e;
- var decimals = [];
- isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
+ var fraction = ('' + number).split(DECIMAL_SEP);
+ var whole = fraction[0];
+ fraction = fraction[1] || '';
- // pad zeros for small numbers
- while (integerLen < 0) {
- digits.unshift(0);
- integerLen++;
- }
+ var i, pos = 0,
+ lgroup = pattern.lgSize,
+ group = pattern.gSize;
- // extract decimals digits
- if (integerLen > 0) {
- decimals = digits.splice(integerLen);
- } else {
- decimals = digits;
- digits = [0];
+ 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);
+ }
}
- // 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(''));
+ for (i = pos; i < whole.length; i++) {
+ if ((whole.length - i) % lgroup === 0 && i !== 0) {
+ formatedText += groupSep;
+ }
+ formatedText += whole.charAt(i);
}
- formattedText = groups.join(groupSep);
- // append the decimal digits
- if (decimals.length) {
- formattedText += decimalSep + decimals.join('');
+ // format fraction part.
+ while (fraction.length < fractionSize) {
+ fraction += '0';
}
- if (exponent) {
- formattedText += 'e+' + exponent;
+ if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
+ } else {
+ if (fractionSize > 0 && number < 1) {
+ formatedText = number.toFixed(fractionSize);
+ number = parseFloat(formatedText);
+ formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
}
}
- if (number < 0 && !isZero) {
- return pattern.negPre + formattedText + pattern.negSuf;
- } else {
- return pattern.posPre + formattedText + pattern.posSuf;
+
+ if (number === 0) {
+ isNegative = false;
}
+
+ parts.push(isNegative ? pattern.negPre : pattern.posPre,
+ formatedText,
+ isNegative ? pattern.negSuf : pattern.posSuf);
+ return parts.join('');
}
function padNumber(num, digits, trim) {
num = -num;
}
num = '' + num;
- while (num.length < digits) num = ZERO_CHAR + num;
+ while (num.length < digits) num = '0' + num;
if (trim) {
num = num.substr(num.length - digits);
}
var dateTimezoneOffset = date.getTimezoneOffset();
if (timezone) {
- dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
+ dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
date = convertTimezoneToLocal(date, timezone, true);
}
forEach(parts, function(value) {
fn = DATE_FORMATS[value];
text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
- : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
+ : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
});
return text;
* `reverse` is not set, which means it defaults to `false`.
<example module="orderByExample">
<file name="index.html">
+ <script>
+ 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}];
+ }]);
+ </script>
<div ng-controller="ExampleController">
<table class="friend">
<tr>
</table>
</div>
</file>
- <file name="script.js">
- 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}];
- }]);
- </file>
</example>
*
* The predicate and reverse parameters can be controlled dynamically through scope properties,
* @example
<example module="orderByExample">
<file name="index.html">
+ <script>
+ 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}];
+ $scope.predicate = 'age';
+ $scope.reverse = true;
+ $scope.order = function(predicate) {
+ $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
+ $scope.predicate = predicate;
+ };
+ }]);
+ </script>
+ <style type="text/css">
+ .sortorder:after {
+ content: '\25b2';
+ }
+ .sortorder.reverse:after {
+ content: '\25bc';
+ }
+ </style>
<div ng-controller="ExampleController">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
- <button ng-click="predicate=''">Set to unsorted</button>
+ [ <a href="" ng-click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
- <th>
- <button ng-click="order('name')">Name</button>
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('phone')">Phone Number</button>
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('age')">Age</button>
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
- </th>
+ <th>
+ <a href="" ng-click="order('name')">Name</a>
+ <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
+ </th>
+ <th>
+ <a href="" ng-click="order('phone')">Phone Number</a>
+ <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
+ </th>
+ <th>
+ <a href="" ng-click="order('age')">Age</a>
+ <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
+ </th>
</tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
</table>
</div>
</file>
- <file name="script.js">
- 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}];
- $scope.predicate = 'age';
- $scope.reverse = true;
- $scope.order = function(predicate) {
- $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
- $scope.predicate = predicate;
- };
- }]);
- </file>
- <file name="style.css">
- .sortorder:after {
- content: '\25b2';
- }
- .sortorder.reverse:after {
- content: '\25bc';
- }
- </file>
</example>
*
* It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
* @example
<example module="orderByExample">
<file name="index.html">
- <div ng-controller="ExampleController">
- <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
- <table class="friend">
- <tr>
- <th>
- <button ng-click="order('name')">Name</button>
- <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('phone')">Phone Number</button>
- <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
- </th>
- <th>
- <button ng-click="order('age')">Age</button>
- <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
- </th>
- </tr>
- <tr ng-repeat="friend in friends">
- <td>{{friend.name}}</td>
- <td>{{friend.phone}}</td>
- <td>{{friend.age}}</td>
- </tr>
- </table>
- </div>
+ <div ng-controller="ExampleController">
+ <table class="friend">
+ <tr>
+ <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
+ (<a href="" ng-click="order('-name',false)">^</a>)</th>
+ <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
+ <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
+ </tr>
+ <tr ng-repeat="friend in friends">
+ <td>{{friend.name}}</td>
+ <td>{{friend.phone}}</td>
+ <td>{{friend.age}}</td>
+ </tr>
+ </table>
+ </div>
</file>
<file name="script.js">
{ name: 'Adam', phone: '555-5678', age: 35 },
{ name: 'Julie', phone: '555-8765', age: 29 }
];
- $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 = function(predicate, reverse) {
+ $scope.friends = orderBy($scope.friends, predicate, reverse);
};
- $scope.order('age', true);
+ $scope.order('-age',false);
}]);
</file>
-
- <file name="style.css">
- .sortorder:after {
- content: '\25b2';
- }
- .sortorder.reverse:after {
- content: '\25bc';
- }
- </file>
</example>
*/
orderByFilter.$inject = ['$parse'];
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
*
* A special directive is necessary because we cannot use interpolation inside the `disabled`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ * attribute. The following example would make the button enabled on Chrome/Firefox
+ * but not on older IEs:
+ *
+ * ```html
+ * <!-- See below for an example of ng-disabled being used correctly -->
+ * <div ng-init="isDisabled = false">
+ * <button disabled="{{isDisabled}}">Disabled</button>
+ * </div>
+ * ```
+ *
+ * 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.
*
* @example
<example>
* Note that this directive should not be used together with {@link ngModel `ngModel`},
* as this can lead to unexpected behavior.
*
- * A special directive is necessary because we cannot use interpolation inside the `checked`
- * attribute. See the {@link guide/interpolation interpolation guide} for more info.
+ * ### Why do we need `ngChecked`?
*
+ * 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
<example>
<file name="index.html">
* @priority 100
*
* @description
- *
- * 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.
- *
+ * 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.
* @example
<example>
<file name="index.html">
* @priority 100
*
* @description
- *
- * 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.
+ * 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.
*
* @example
<example>
* @priority 100
*
* @description
- *
- * 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.
- *
+ * 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.
* @example
<example>
<file name="index.html">
*
* 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 `<form>` elements, so
- * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
- * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
- * of controls needs to be determined.
+ * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
+ * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
+ * using Angular validation directives in forms that are dynamically generated using the
+ * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
+ * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
+ * `ngForm` directive and nest these in an outer `form` element.
+ *
*
* # CSS classes
* - `ng-valid` is set if the form is valid.
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
-// Note: We are being more lenient, because browsers are too.
-// 1. Scheme
-// 2. Slashes
-// 3. Username
-// 4. Password
-// 5. Hostname
-// 6. Port
-// 7. Path
-// 8. Query
-// 9. Fragment
-// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
-var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
+var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
-var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
-var PARTIAL_VALIDATION_TYPES = createMap();
-forEach('date,datetime-local,month,time,week'.split(','), function(type) {
- PARTIAL_VALIDATION_TYPES[type] = true;
-});
-
var inputType = {
/**
});
}
- var timeout;
-
var listener = function(ev) {
if (timeout) {
$browser.defer.cancel(timeout);
if ($sniffer.hasEvent('input')) {
element.on('input', listener);
} else {
+ var timeout;
+
var deferListener = function(ev, input, origValue) {
if (!timeout) {
timeout = $browser.defer(function() {
// or form autocomplete on newer browser, we need "change" event to catch it
element.on('change', listener);
- // Some native input types (date-family) have the ability to change validity without
- // firing any input/change events.
- // For these event types, when native validators are present and the browser supports the type,
- // check for validity changes on various DOM events.
- if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
- element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
- if (!timeout) {
- var validity = this[VALIDITY_STATE_PROPERTY];
- var origBadInput = validity.badInput;
- var origTypeMismatch = validity.typeMismatch;
- timeout = $browser.defer(function() {
- timeout = null;
- if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
- listener(ev);
- }
- });
- }
- });
- }
-
ctrl.$render = function() {
// Workaround for Firefox validation #12102.
var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
//set the 2nd param to true to ignore the template request error so that the inner
//contents and scope can be cleaned up.
$templateRequest(src, true).then(function(response) {
- if (scope.$$destroyed) return;
-
if (thisChangeId !== changeCounter) return;
var newScope = scope.$new();
ctrl.template = response;
currentScope.$emit('$includeContentLoaded', src);
scope.$eval(onloadExp);
}, function() {
- if (scope.$$destroyed) return;
-
if (thisChangeId === changeCounter) {
cleanupLastIncludeContent();
scope.$emit('$includeContentError', src);
* which may be caused by a pending debounced event or because the input is waiting for a some
* future event.
*
- * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
- * depend on special events such as blur, you can have a situation where there is a period when
- * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
- *
- * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
- * and reset the input to the last committed view value.
+ * If you have an input that uses `ng-model-options` to set up debounced events or events such
+ * as blur you can have a situation where there is a period when the `$viewValue`
+ * is out of synch with the ngModel's `$modelValue`.
*
- * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
+ * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
* programmatically before these debounced/future events have resolved/occurred, because Angular's
* dirty checking mechanism is not able to tell whether the model has actually changed or not.
*
* angular.module('cancel-update-example', [])
*
* .controller('CancelUpdateController', ['$scope', function($scope) {
- * $scope.model = {};
- *
- * $scope.setEmpty = function(e, value, rollback) {
+ * $scope.resetWithCancel = function(e) {
+ * if (e.keyCode == 27) {
+ * $scope.myForm.myInput1.$rollbackViewValue();
+ * $scope.myValue = '';
+ * }
+ * };
+ * $scope.resetWithoutCancel = function(e) {
* if (e.keyCode == 27) {
- * e.preventDefault();
- * if (rollback) {
- * $scope.myForm[value].$rollbackViewValue();
- * }
- * $scope.model[value] = '';
+ * $scope.myValue = '';
* }
* };
* }]);
* </file>
* <file name="index.html">
* <div ng-controller="CancelUpdateController">
- * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
- * empty them. Follow these steps and observe the difference:</p>
- * <ol>
- * <li>Type something in the input. You will see that the model is not yet updated</li>
- * <li>Press the Escape key.
- * <ol>
- * <li> In the first example, nothing happens, because the model is already '', and no
- * update is detected. If you blur the input, the model will be set to the current view.
- * </li>
- * <li> In the second example, the pending update is cancelled, and the input is set back
- * to the last committed view value (''). Blurring the input does nothing.
- * </li>
- * </ol>
- * </li>
- * </ol>
+ * <p>Try typing something in each input. See that the model only updates when you
+ * blur off the input.
+ * </p>
+ * <p>Now see what happens if you start typing then press the Escape key</p>
*
* <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
- * <div>
- * <p id="inputDescription1">Without $rollbackViewValue():</p>
- * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
- * ng-keydown="setEmpty($event, 'value1')">
- * value1: "{{ model.value1 }}"
- * </div>
- *
- * <div>
- * <p id="inputDescription2">With $rollbackViewValue():</p>
- * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
- * ng-keydown="setEmpty($event, 'value2', true)">
- * value2: "{{ model.value2 }}"
- * </div>
+ * <p id="inputDescription1">With $rollbackViewValue()</p>
+ * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
+ * ng-keydown="resetWithCancel($event)"><br/>
+ * myValue: "{{ myValue }}"
+ *
+ * <p id="inputDescription2">Without $rollbackViewValue()</p>
+ * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
+ * ng-keydown="resetWithoutCancel($event)"><br/>
+ * myValue: "{{ myValue }}"
* </form>
* </div>
* </file>
- <file name="style.css">
- div {
- display: table-cell;
- }
- div:nth-child(1) {
- padding-right: 30px;
- }
-
- </file>
* </example>
*/
this.$rollbackViewValue = function() {
forEach(ctrl.$asyncValidators, function(validator, name) {
var promise = validator(modelValue, viewValue);
if (!isPromiseLike(promise)) {
- throw ngModelMinErr('nopromise',
+ throw ngModelMinErr("$asyncValidators",
"Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
}
setValidity(name, undefined);
* - {@link ng.directive:select select}
* - {@link ng.directive:textarea textarea}
*
- * # Complex Models (objects or collections)
- *
- * By default, `ngModel` watches the model by reference, not value. This is important to know when
- * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
- * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
- *
- * The model must be assigned an entirely new object or collection before a re-rendering will occur.
- *
- * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
- * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
- * if the select is given the `multiple` attribute.
- *
- * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
- * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
- * not trigger a re-rendering of the model.
- *
* # CSS classes
* The following CSS classes are added and removed on the associated input/select/textarea element
* depending on the validity of the model.
var option = options.getOptionFromViewValue(value);
if (option && !option.disabled) {
- // Don't update the option when it is already selected.
- // For example, the browser will select the first option by default. In that case,
- // most properties are set automatically - except the `selected` attribute, which we
- // set always
-
if (selectElement[0].value !== option.selectValue) {
removeUnknownOption();
removeEmptyOption();
selectElement[0].value = option.selectValue;
option.element.selected = true;
+ option.element.setAttribute('selected', 'selected');
}
-
- option.element.setAttribute('selected', 'selected');
} else {
if (value === null || providedEmptyOption) {
removeUnknownOption();
(current === emptyOption_ ||
current === unknownOption_ ||
current.nodeType === NODE_TYPE_COMMENT ||
- (nodeName_(current) === 'option' && current.value === ''))) {
+ current.value === '')) {
current = current.nextSibling;
}
}
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
- var isNotPrimitive = ngOptions.trackBy || multiple;
- if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
+ if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
ngModelCtrl.$setViewValue(nextValue);
ngModelCtrl.$render();
}
if (getBlockStart(block) != nextNode) {
// existing item which got moved
- $animate.move(getBlockNodes(block.clone), null, previousNode);
+ $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
}
previousNode = getBlockEnd(block);
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
var endNode = ngRepeatEndComment.cloneNode(false);
clone[clone.length++] = endNode;
- $animate.enter(clone, null, previousNode);
+ // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
+ $animate.enter(clone, null, jqLite(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
// Tell the select control that an option, with the given value, has been added
self.addOption = function(value, element) {
- // Skip comment nodes, as they only pollute the `optionsMap`
- if (element[0].nodeType === NODE_TYPE_COMMENT) return;
-
assertNotHasOwnProperty(value, '"option value"');
if (value === '') {
self.emptyOption = element;
*
* The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
* between the scope and the `<select>` control (including setting default values).
- * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
+ * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
* {@link ngOptions `ngOptions`} directives.
*
* When an item in the `<select>` menu is selected, the value of the selected option will be bound
controller: SelectController,
priority: 1,
link: {
- pre: selectPreLink,
- post: selectPostLink
+ pre: selectPreLink
}
};
selectCtrl.ngModelCtrl = ngModelCtrl;
+ // We delegate rendering to the `writeValue` method, which can be changed
+ // if the select can have multiple selected values or if the options are being
+ // generated by `ngOptions`
+ ngModelCtrl.$render = function() {
+ selectCtrl.writeValue(ngModelCtrl.$viewValue);
+ };
+
// When the selected item(s) changes we delegate getting the value of the select control
// to the `readValue` method, which can be changed if the select can have multiple
// selected values or if the options are being generated by `ngOptions`
}
}
-
- function selectPostLink(scope, element, attrs, ctrls) {
- // if ngModel is not defined, we don't need to do anything
- var ngModelCtrl = ctrls[1];
- if (!ngModelCtrl) return;
-
- var selectCtrl = ctrls[0];
-
- // We delegate rendering to the `writeValue` method, which can be changed
- // if the select can have multiple selected values or if the options are being
- // generated by `ngOptions`.
- // This must be done in the postLink fn to prevent $render to be called before
- // all nodes have been linked correctly.
- ngModelCtrl.$render = function() {
- selectCtrl.writeValue(ngModelCtrl.$viewValue);
- };
- }
};
restrict: 'E',
priority: 100,
compile: function(element, attr) {
+
if (isDefined(attr.value)) {
// If the value attribute is defined, check if it contains an interpolation
var interpolateValueFn = $interpolate(attr.value, true);
}
return function(scope, element, attr) {
+
// This is an optimization over using ^^ since we don't want to have to search
// all the way to the root of the DOM for every single option element
var selectCtrlName = '$selectController',
terminal: false
});
-/**
- * @ngdoc directive
- * @name ngRequired
- *
- * @description
- *
- * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
- * applied to custom controls.
- *
- * The directive sets the `required` attribute on the element if the Angular expression inside
- * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
- * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
- * for more info.
- *
- * The validator will set the `required` error key to true if the `required` attribute is set and
- * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
- * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
- * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
- * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
- *
- * @example
- * <example name="ngRequiredDirective" module="ngRequiredExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngRequiredExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.required = true;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="required">Toggle required: </label>
- * <input type="checkbox" ng-model="required" id="required" />
- * <br>
- * <label for="input">This input must be filled if `required` is true: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
- * <hr>
- * required error set? = <code>{{form.input.$error.required}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var required = element(by.binding('form.input.$error.required'));
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should set the required error', function() {
- expect(required.getText()).toContain('true');
-
- input.sendKeys('123');
- expect(required.getText()).not.toContain('true');
- expect(model.getText()).toContain('123');
- });
- * </file>
- * </example>
- */
var requiredDirective = function() {
return {
restrict: 'A',
};
};
-/**
- * @ngdoc directive
- * @name ngPattern
- *
- * @description
- *
- * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * does not match a RegExp which is obtained by evaluating the Angular expression given in the
- * `ngPattern` attribute value:
- * * If the expression evaluates to a RegExp object, then this is used directly.
- * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
- * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
- *
- * <div class="alert alert-info">
- * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
- * start at the index of the last search's match, thus not taking the whole input value into
- * account.
- * </div>
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
- * not available.
- * </li>
- * <li>
- * The `ngPattern` attribute must be an expression, while the `pattern` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngPatternDirective" module="ngPatternExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngPatternExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.regex = '\\d+';
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="regex">Set a pattern (regex string): </label>
- * <input type="text" ng-model="regex" id="regex" />
- * <br>
- * <label for="input">This input is restricted by the current pattern: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default pattern', function() {
- input.sendKeys('aaa');
- expect(model.getText()).not.toContain('aaa');
- input.clear().then(function() {
- input.sendKeys('123');
- expect(model.getText()).toContain('123');
- });
- });
- * </file>
- * </example>
- */
var patternDirective = function() {
return {
restrict: 'A',
};
};
-/**
- * @ngdoc directive
- * @name ngMaxlength
- *
- * @description
- *
- * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is longer than the integer obtained by evaluating the Angular expression given in the
- * `ngMaxlength` attribute value.
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
- * validation is not available.
- * </li>
- * <li>
- * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngMaxlengthExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.maxlength = 5;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="maxlength">Set a maxlength: </label>
- * <input type="number" ng-model="maxlength" id="maxlength" />
- * <br>
- * <label for="input">This input is restricted by the current maxlength: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default maxlength', function() {
- input.sendKeys('abcdef');
- expect(model.getText()).not.toContain('abcdef');
- input.clear().then(function() {
- input.sendKeys('abcde');
- expect(model.getText()).toContain('abcde');
- });
- });
- * </file>
- * </example>
- */
var maxlengthDirective = function() {
return {
restrict: 'A',
};
};
-/**
- * @ngdoc directive
- * @name ngMinlength
- *
- * @description
- *
- * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
- * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
- *
- * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is shorter than the integer obtained by evaluating the Angular expression given in the
- * `ngMinlength` attribute value.
- *
- * <div class="alert alert-info">
- * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
- * differences:
- * <ol>
- * <li>
- * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
- * validation is not available.
- * </li>
- * <li>
- * The `ngMinlength` value must be an expression, while the `minlength` value must be
- * interpolated.
- * </li>
- * </ol>
- * </div>
- *
- * @example
- * <example name="ngMinlengthDirective" module="ngMinlengthExample">
- * <file name="index.html">
- * <script>
- * angular.module('ngMinlengthExample', [])
- * .controller('ExampleController', ['$scope', function($scope) {
- * $scope.minlength = 3;
- * }]);
- * </script>
- * <div ng-controller="ExampleController">
- * <form name="form">
- * <label for="minlength">Set a minlength: </label>
- * <input type="number" ng-model="minlength" id="minlength" />
- * <br>
- * <label for="input">This input is restricted by the current minlength: </label>
- * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
- * <hr>
- * input valid? = <code>{{form.input.$valid}}</code><br>
- * model = <code>{{model}}</code>
- * </form>
- * </div>
- * </file>
- * <file name="protractor.js" type="protractor">
- var model = element(by.binding('model'));
- var input = element(by.id('input'));
-
- it('should validate the input with the default minlength', function() {
- input.sendKeys('ab');
- expect(model.getText()).not.toContain('ab');
-
- input.sendKeys('abc');
- expect(model.getText()).toContain('abc');
- });
- * </file>
- * </example>
- */
var minlengthDirective = function() {
return {
restrict: 'A',
if (window.angular.bootstrap) {
//AngularJS is already loaded, so we can return here...
- if (window.console) {
- console.log('WARNING: Tried to load angular more than once.');
- }
+ console.log('WARNING: Tried to load angular more than once.');
return;
}
"Nov",
"Dec"
],
- "STANDALONEMONTH": [
- "January",
- "February",
- "March",
- "April",
- "May",
- "June",
- "July",
- "August",
- "September",
- "October",
- "November",
- "December"
- ],
"WEEKENDRANGE": [
5,
6
]
},
"id": "en-us",
- "localeID": "en_US",
"pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
});
}]);