public/js/angular.js in engine2-1.0.2 vs public/js/angular.js in engine2-1.0.3
- old
+ new
@@ -1,11 +1,11 @@
/**
- * @license AngularJS v1.5.3
+ * @license AngularJS v1.5.5
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
-(function(window, document, undefined) {'use strict';
+(function(window) {'use strict';
/**
* @description
*
* This object provides a utility for producing rich Error messages within
@@ -55,11 +55,11 @@
}
return match;
});
- message += '\nhttp://errors.angularjs.org/1.5.3/' +
+ message += '\nhttp://errors.angularjs.org/1.5.5/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
encodeURIComponent(toDebugString(templateArgs[i]));
@@ -169,10 +169,11 @@
/**
* @ngdoc module
* @name ng
* @module ng
+ * @installation
* @description
*
* # ng (core module)
* The ng module is loaded by default when an AngularJS application is started. The module itself
* contains the essential components for an AngularJS application to function. The table below
@@ -235,11 +236,11 @@
/**
* documentMode is an IE-only property
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
*/
-msie = document.documentMode;
+msie = window.document.documentMode;
/**
* @private
* @param {*} obj
@@ -1045,10 +1046,45 @@
* Scope and DOMWindow objects are being compared only by identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
* @returns {boolean} True if arguments are equal.
+ *
+ * @example
+ <example module="equalsExample" name="equalsExample">
+ <file name="index.html">
+ <div ng-controller="ExampleController">
+ <form novalidate>
+ <h3>User 1</h3>
+ Name: <input type="text" ng-model="user1.name">
+ Age: <input type="number" ng-model="user1.age">
+
+ <h3>User 2</h3>
+ Name: <input type="text" ng-model="user2.name">
+ Age: <input type="number" ng-model="user2.age">
+
+ <div>
+ <br/>
+ <input type="button" value="Compare" ng-click="compare()">
+ </div>
+ User 1: <pre>{{user1 | json}}</pre>
+ User 2: <pre>{{user2 | json}}</pre>
+ Equal: <pre>{{result}}</pre>
+ </form>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
+ $scope.user1 = {};
+ $scope.user2 = {};
+ $scope.result;
+ $scope.compare = function() {
+ $scope.result = angular.equals($scope.user1, $scope.user2);
+ };
+ }]);
+ </file>
+ </example>
*/
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
@@ -1091,12 +1127,12 @@
var csp = function() {
if (!isDefined(csp.rules)) {
- var ngCspElement = (document.querySelector('[ng-csp]') ||
- document.querySelector('[data-ng-csp]'));
+ var ngCspElement = (window.document.querySelector('[ng-csp]') ||
+ window.document.querySelector('[data-ng-csp]'));
if (ngCspElement) {
var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
ngCspElement.getAttribute('data-ng-csp');
csp.rules = {
@@ -1167,11 +1203,11 @@
if (isDefined(jq.name_)) return jq.name_;
var el;
var i, ii = ngAttrPrefixes.length, prefix, name;
for (i = 0; i < ii; ++i) {
prefix = ngAttrPrefixes[i];
- if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
+ if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
name = el.getAttribute(prefix + 'jq');
break;
}
}
@@ -1232,11 +1268,11 @@
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
- } else if (value && document === value) {
+ } else if (value && window.document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
@@ -1684,15 +1720,15 @@
config = extend(defaultConfig, config);
var doBootstrap = function() {
element = jqLite(element);
if (element.injector()) {
- var tag = (element[0] === document) ? 'document' : startingTag(element);
+ var tag = (element[0] === window.document) ? 'document' : startingTag(element);
//Encode angle brackets to prevent input from being sanitized to empty string #8683
throw ngMinErr(
'btstrpd',
- "App Already Bootstrapped with this Element '{0}'",
+ "App already bootstrapped with this element '{0}'",
tag.replace(/</,'<').replace(/>/,'>'));
}
modules = modules || [];
modules.unshift(['$provide', function($provide) {
@@ -2135,13 +2171,13 @@
/**
* @ngdoc method
* @name angular.Module#decorator
* @module ng
- * @param {string} The name of the service to decorate.
- * @param {Function} This function will be invoked when the service needs to be
- * instantiated and should return the decorated service instance.
+ * @param {string} name The name of the service to decorate.
+ * @param {Function} decorFn This function will be invoked when the service needs to be
+ * instantiated and should return the decorated service instance.
* @description
* See {@link auto.$provide#decorator $provide.decorator()}.
*/
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
@@ -2441,15 +2477,15 @@
* - `minor` – `{number}` – Minor version number, such as "9".
* - `dot` – `{number}` – Dot version number, such as "18".
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.5.3', // all of these placeholder strings will be replaced by grunt's
+ full: '1.5.5', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 5,
- dot: 3,
- codeName: 'diplohaplontic-meiosis'
+ dot: 5,
+ codeName: 'material-conspiration'
};
function publishExternalAPI(angular) {
extend(angular, {
@@ -2702,10 +2738,13 @@
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
* Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
+ * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
+ * https://github.com/angular/angular.js/issues/14251 for more information.
+ *
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
* @returns {Object} jQuery object.
*/
JQLite.expando = 'ng339';
@@ -2828,11 +2867,11 @@
return fragment;
}
function jqLiteParseHTML(html, context) {
- context = context || document;
+ context = context || window.document;
var parsed;
if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
return [context.createElement(parsed[1])];
}
@@ -2854,11 +2893,11 @@
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) {
+var jqLiteContains = window.Node.prototype.contains || function(arg) {
// jshint bitwise: false
return !!(this.compareDocumentPosition(arg) & 16);
// jshint bitwise: true
};
@@ -3126,12 +3165,12 @@
fired = true;
fn();
}
// check if document is already loaded
- if (document.readyState === 'complete') {
- setTimeout(trigger);
+ if (window.document.readyState === 'complete') {
+ window.setTimeout(trigger);
} else {
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
// jshint -W064
JQLite(window).on('load', trigger); // fallback to window.onload for others
@@ -3817,10 +3856,11 @@
/**
* @ngdoc module
* @name auto
+ * @installation
* @description
*
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/
@@ -3830,11 +3870,11 @@
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function extractArgs(fn) {
- var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
+ var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
return args;
}
function anonFn(fn) {
@@ -5250,19 +5290,24 @@
*
* ```js
* // remove all the animation event listeners listening for `enter`
* $animate.off('enter');
*
+ * // remove listeners for all animation events from the container element
+ * $animate.off(container);
+ *
* // remove all the animation event listeners listening for `enter` on the given element and its children
* $animate.off('enter', container);
*
* // remove the event listener function provided by `callback` that is set
* // to listen for `enter` on the given `container` as well as its children
* $animate.off('enter', container, callback);
* ```
*
- * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
+ * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
+ * addClass, removeClass, etc...), or the container element. If it is the element, all other
+ * arguments are ignored.
* @param {DOMElement=} container the container element the event listener was placed on
* @param {Function=} callback the callback function that was registered as the listener
*/
off: $$animateQueue.off,
@@ -6831,12 +6876,12 @@
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre & post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
- * object of the form `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component
- * such as cloning the bound value to prevent accidental mutation of the outer value.
+ * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
+ * component such as cloning the bound value to prevent accidental mutation of the outer value.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
@@ -7379,10 +7424,13 @@
* {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
*/
var $compileMinErr = minErr('$compile');
+function UNINITIALIZED_VALUE() {}
+var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
+
/**
* @ngdoc provider
* @name $compileProvider
*
* @description
@@ -7403,11 +7451,11 @@
var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
- var bindings = {};
+ var bindings = createMap();
forEach(scope, function(definition, scopeName) {
if (definition in bindingCache) {
bindings[scopeName] = bindingCache[definition];
return;
@@ -7577,10 +7625,13 @@
* - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
* Component properties are always bound to the component controller and not to the scope.
* See {@link ng.$compile#-bindtocontroller- `bindToController`}.
* - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
* Disabled by default.
+ * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
+ * this component's controller. The object keys specify the property names under which the required
+ * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
* - `$...` – additional properties to attach to the directive factory function and the controller
* constructor function. (This is used by the component router to annotate)
*
* @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
* @description
@@ -7622,11 +7673,11 @@
*
* <br />
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
this.component = function registerComponent(name, options) {
- var controller = options.controller || noop;
+ var controller = options.controller || function() {};
function factory($injector) {
function makeInjectable(fn) {
if (isFunction(fn) || isArray(fn)) {
return function(tElement, tAttrs) {
@@ -7636,29 +7687,42 @@
return fn;
}
}
var template = (!options.template && !options.templateUrl ? '' : options.template);
- return {
+ var ddo = {
controller: controller,
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
template: makeInjectable(template),
templateUrl: makeInjectable(options.templateUrl),
transclude: options.transclude,
scope: {},
bindToController: options.bindings || {},
restrict: 'E',
require: options.require
};
+
+ // Copy annotations (starting with $) over to the DDO
+ forEach(options, function(val, key) {
+ if (key.charAt(0) === '$') ddo[key] = val;
+ });
+
+ return ddo;
}
- // Copy any annotation properties (starting with $) over to the factory function
+ // TODO(pete) remove the following `forEach` before we release 1.6.0
+ // The component-router@0.2.0 looks for the annotations on the controller constructor
+ // Nothing in Angular looks for annotations on the factory function but we can't remove
+ // it from 1.5.x yet.
+
+ // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
// These could be used by libraries such as the new component router
forEach(options, function(val, key) {
if (key.charAt(0) === '$') {
factory[key] = val;
- controller[key] = val;
+ // Don't try to copy over annotations to named controller
+ if (isFunction(controller)) controller[key] = val;
}
});
factory.$inject = ['$injector'];
@@ -7791,11 +7855,11 @@
'$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
$controller, $rootScope, $sce, $animate, $$sanitizeUri) {
var SIMPLE_ATTR_NAME = /^\w/;
- var specialAttrHolder = document.createElement('div');
+ var specialAttrHolder = window.document.createElement('div');
var onChangesTtl = TTL;
// The onChanges hooks should all be run together in a single digest
@@ -8122,11 +8186,11 @@
compile.$$createComment = function(directiveName, comment) {
var content = '';
if (debugInfoEnabled) {
content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
}
- return document.createComment(content);
+ return window.document.createComment(content);
};
return compile;
//================================
@@ -8145,11 +8209,11 @@
// 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'));
+ jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
}
}
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
@@ -8838,11 +8902,13 @@
if (directive.replace) {
replaceDirective = directive;
}
+ /* jshint -W021 */
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
+ /* jshint +W021 */
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
controllerDirectives: controllerDirectives,
newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
newIsolateScopeDirective: newIsolateScopeDirective,
templateDirective: templateDirective,
@@ -8902,11 +8968,11 @@
}
}
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
- attrs, removeScopeBindingWatches, removeControllerBindingWatches;
+ attrs, scopeBindingInfo;
if (compileNode === linkNode) {
attrs = templateAttrs;
$element = templateAttrs.$$element;
} else {
@@ -8941,37 +9007,39 @@
compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
templateDirective === newIsolateScopeDirective.$$originalDirective)));
compile.$$addScopeClass($element, true);
isolateScope.$$isolateBindings =
newIsolateScopeDirective.$$isolateBindings;
- removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
+ scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
isolateScope.$$isolateBindings,
newIsolateScopeDirective);
- if (removeScopeBindingWatches) {
- isolateScope.$on('$destroy', removeScopeBindingWatches);
+ if (scopeBindingInfo.removeWatches) {
+ isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
}
}
// Initialize bindToController bindings
for (var name in elementControllers) {
var controllerDirective = controllerDirectives[name];
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;
if (controller.identifier && bindings) {
- removeControllerBindingWatches =
+ controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
+ } else {
+ controller.bindingInfo = {};
}
var controllerResult = controller();
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers
controller.instance = controllerResult;
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
- removeControllerBindingWatches && removeControllerBindingWatches();
- removeControllerBindingWatches =
+ controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
+ controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
}
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
@@ -8983,10 +9051,13 @@
});
// Handle the init and destroy lifecycle hooks on all controllers that have them
forEach(elementControllers, function(controller) {
var controllerInstance = controller.instance;
+ if (isFunction(controllerInstance.$onChanges)) {
+ controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
+ }
if (isFunction(controllerInstance.$onInit)) {
controllerInstance.$onInit();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
@@ -9439,11 +9510,11 @@
function wrapTemplate(type, template) {
type = lowercase(type || 'html');
switch (type) {
case 'svg':
case 'math':
- var wrapper = document.createElement('div');
+ var wrapper = window.document.createElement('div');
wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
return wrapper.childNodes[0].childNodes;
default:
return template;
}
@@ -9583,11 +9654,11 @@
// Append all the `elementsToRemove` to a fragment. This will...
// - remove them from the DOM
// - allow them to still be traversed with .nextSibling
// - allow a single fragment.qSA to fetch all elements being removed
- var fragment = document.createDocumentFragment();
+ var fragment = window.document.createDocumentFragment();
for (i = 0; i < removeCount; i++) {
fragment.appendChild(elementsToRemove[i]);
}
if (jqLite.hasData(firstElementToRemove)) {
@@ -9629,10 +9700,11 @@
// Set up $watches for isolate scope and controller bindings. This process
// only occurs for isolate scopes and new scopes with controllerAs.
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
+ var initialChanges = {};
var changes;
forEach(bindings, function initializeBinding(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
@@ -9644,11 +9716,11 @@
case '@':
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
destination[scopeName] = attrs[attrName] = void 0;
}
attrs.$observe(attrName, function(value) {
- if (isString(value)) {
+ if (isString(value) || isBoolean(value)) {
var oldValue = destination[scopeName];
recordChanges(scopeName, value, oldValue);
destination[scopeName] = value;
}
});
@@ -9661,10 +9733,11 @@
} 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;
}
+ initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
break;
case '=':
if (!hasOwnProperty.call(attrs, attrName)) {
if (optional) break;
@@ -9716,15 +9789,20 @@
if (optional && !attrs[attrName]) break;
parentGet = $parse(attrs[attrName]);
destination[scopeName] = parentGet(scope);
+ initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
- removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
- var oldValue = destination[scopeName];
- recordChanges(scopeName, newParentValue, oldValue);
- destination[scopeName] = newParentValue;
+ removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
+ if (newValue === oldValue) {
+ // If the new and old values are identical then this is the first time the watch has been triggered
+ // So instead we use the current value on the destination as the old value
+ oldValue = destination[scopeName];
+ }
+ recordChanges(scopeName, newValue, oldValue);
+ destination[scopeName] = newValue;
}, parentGet.literal);
removeWatchCollection.push(removeWatch);
break;
@@ -9757,29 +9835,39 @@
// If the has been a change on this property already then we need to reuse the previous value
if (changes[key]) {
previousValue = changes[key].previousValue;
}
// Store this change
- changes[key] = {previousValue: previousValue, currentValue: currentValue};
+ changes[key] = new SimpleChange(previousValue, currentValue);
}
}
function triggerOnChangesHook() {
destination.$onChanges(changes);
// Now clear the changes so that we schedule onChanges when more changes arrive
changes = undefined;
}
- return removeWatchCollection.length && function removeWatches() {
- for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
- removeWatchCollection[i]();
+ return {
+ initialChanges: initialChanges,
+ removeWatches: removeWatchCollection.length && function removeWatches() {
+ for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
+ removeWatchCollection[i]();
+ }
}
};
}
}];
}
+function SimpleChange(previous, current) {
+ this.previousValue = previous;
+ this.currentValue = current;
+}
+SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
+
+
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
/**
* Converts all accepted directives format into proper directive name.
* @param name Name to normalize
*/
@@ -10715,11 +10803,11 @@
* <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.
+ * To prevent this, 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
*
@@ -10961,10 +11049,16 @@
* with the `paramSerializer` and appended as GET parameters.
* - **data** – `{string|Object}` – Data to be sent as the request message data.
* - **headers** – `{Object}` – Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function is null, the
* header will not be sent. Functions accept a config object as an argument.
+ * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
+ * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
+ * The handler will be called in the context of a `$apply` block.
+ * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
+ * object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
+ * The handler will be called in the context of a `$apply` block.
* - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
* - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
* - **transformRequest** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
@@ -11419,16 +11513,40 @@
if (xsrfValue) {
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
}
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
- config.withCredentials, config.responseType);
+ config.withCredentials, config.responseType,
+ createApplyHandlers(config.eventHandlers),
+ createApplyHandlers(config.uploadEventHandlers));
}
return promise;
+ function createApplyHandlers(eventHandlers) {
+ if (eventHandlers) {
+ var applyHandlers = {};
+ forEach(eventHandlers, function(eventHandler, key) {
+ applyHandlers[key] = function(event) {
+ if (useApplyAsync) {
+ $rootScope.$applyAsync(callEventHandler);
+ } else if ($rootScope.$$phase) {
+ callEventHandler();
+ } else {
+ $rootScope.$apply(callEventHandler);
+ }
+ function callEventHandler() {
+ eventHandler(event);
+ }
+ };
+ });
+ return applyHandlers;
+ }
+ }
+
+
/**
* Callback registered to $httpBackend():
* - caches the response if desired
* - resolves the raw $http promise
* - calls $apply
@@ -11544,11 +11662,11 @@
}];
}
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
// TODO(vojta): fix the signature
- return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
+ return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
if (lowercase(method) == 'jsonp') {
var callbackId = '_' + (callbacks.counter++).toString(36);
@@ -11604,10 +11722,18 @@
};
xhr.onerror = requestError;
xhr.onabort = requestError;
+ forEach(eventHandlers, function(value, key) {
+ xhr.addEventListener(key, value);
+ });
+
+ forEach(uploadEventHandlers, function(value, key) {
+ xhr.upload.addEventListener(key, value);
+ });
+
if (withCredentials) {
xhr.withCredentials = true;
}
if (responseType) {
@@ -13582,11 +13708,11 @@
var ch = this.text.charAt(this.index);
if (ch === '"' || ch === "'") {
this.readString(ch);
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
this.readNumber();
- } else if (this.isIdent(ch)) {
+ } else if (this.isIdentifierStart(this.peekMultichar())) {
this.readIdent();
} else if (this.is(ch, '(){}[].,;:?')) {
this.tokens.push({index: this.index, text: ch});
this.index++;
} else if (this.isWhitespace(ch)) {
@@ -13626,16 +13752,53 @@
// IE treats non-breaking space as \u00A0
return (ch === ' ' || ch === '\r' || ch === '\t' ||
ch === '\n' || ch === '\v' || ch === '\u00A0');
},
- isIdent: function(ch) {
+ isIdentifierStart: function(ch) {
+ return this.options.isIdentifierStart ?
+ this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
+ this.isValidIdentifierStart(ch);
+ },
+
+ isValidIdentifierStart: function(ch) {
return ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' === ch || ch === '$');
},
+ isIdentifierContinue: function(ch) {
+ return this.options.isIdentifierContinue ?
+ this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
+ this.isValidIdentifierContinue(ch);
+ },
+
+ isValidIdentifierContinue: function(ch, cp) {
+ return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
+ },
+
+ codePointAt: function(ch) {
+ if (ch.length === 1) return ch.charCodeAt(0);
+ /*jshint bitwise: false*/
+ return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
+ /*jshint bitwise: true*/
+ },
+
+ peekMultichar: function() {
+ var ch = this.text.charAt(this.index);
+ var peek = this.peek();
+ if (!peek) {
+ return ch;
+ }
+ var cp1 = ch.charCodeAt(0);
+ var cp2 = peek.charCodeAt(0);
+ if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
+ return ch + peek;
+ }
+ return ch;
+ },
+
isExpOperator: function(ch) {
return (ch === '-' || ch === '+' || this.isNumber(ch));
},
throwError: function(error, start, end) {
@@ -13680,16 +13843,17 @@
});
},
readIdent: function() {
var start = this.index;
+ this.index += this.peekMultichar().length;
while (this.index < this.text.length) {
- var ch = this.text.charAt(this.index);
- if (!(this.isIdent(ch) || this.isNumber(ch))) {
+ var ch = this.peekMultichar();
+ if (!this.isIdentifierContinue(ch)) {
break;
}
- this.index++;
+ this.index += ch.length;
}
this.tokens.push({
index: start,
text: this.text.slice(start, this.index),
identifier: true
@@ -14615,11 +14779,17 @@
notNull: function(expression) {
return expression + '!=null';
},
nonComputedMember: function(left, right) {
- return left + '.' + right;
+ var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
+ var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
+ if (SAFE_IDENTIFIER.test(right)) {
+ return left + '.' + right;
+ } else {
+ return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
+ }
},
computedMember: function(left, right) {
return left + '[' + right + ']';
},
@@ -15178,10 +15348,11 @@
'true': true,
'false': false,
'null': null,
'undefined': undefined
};
+ var identStart, identContinue;
/**
* @ngdoc method
* @name $parseProvider#addLiteral
* @description
@@ -15194,21 +15365,54 @@
**/
this.addLiteral = function(literalName, literalValue) {
literals[literalName] = literalValue;
};
+ /**
+ * @ngdoc method
+ * @name $parseProvider#setIdentifierFns
+ * @description
+ *
+ * Allows defining the set of characters that are allowed in Angular expressions. The function
+ * `identifierStart` will get called to know if a given character is a valid character to be the
+ * first character for an identifier. The function `identifierContinue` will get called to know if
+ * a given character is a valid character to be a follow-up identifier character. The functions
+ * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
+ * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
+ * mind that the `string` parameter can be two characters long depending on the character
+ * representation. It is expected for the function to return `true` or `false`, whether that
+ * character is allowed or not.
+ *
+ * Since this function will be called extensivelly, keep the implementation of these functions fast,
+ * as the performance of these functions have a direct impact on the expressions parsing speed.
+ *
+ * @param {function=} identifierStart The function that will decide whether the given character is
+ * a valid identifier start character.
+ * @param {function=} identifierContinue The function that will decide whether the given character is
+ * a valid identifier continue character.
+ */
+ this.setIdentifierFns = function(identifierStart, identifierContinue) {
+ identStart = identifierStart;
+ identContinue = identifierContinue;
+ return this;
+ };
+
this.$get = ['$filter', function($filter) {
var noUnsafeEval = csp().noUnsafeEval;
var $parseOptions = {
csp: noUnsafeEval,
expensiveChecks: false,
- literals: copy(literals)
+ literals: copy(literals),
+ isIdentifierStart: isFunction(identStart) && identStart,
+ isIdentifierContinue: isFunction(identContinue) && identContinue
},
$parseOptionsExpensive = {
csp: noUnsafeEval,
expensiveChecks: true,
- literals: copy(literals)
+ literals: copy(literals),
+ isIdentifierStart: isFunction(identStart) && identStart,
+ isIdentifierContinue: isFunction(identContinue) && identContinue
};
var runningChecksEnabled = false;
$parse.$$runningExpensiveChecks = function() {
return runningChecksEnabled;
@@ -18999,11 +19203,11 @@
// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
// cause us to break tests. In addition, when the browser resolves a URL for XHR, it
// doesn't know about mocked locations and resolves URLs to the real document - which is
// exactly the behavior needed here. There is little value is mocking these out for this
// service.
-var urlParsingNode = document.createElement("a");
+var urlParsingNode = window.document.createElement("a");
var originUrl = urlResolve(window.location.href);
/**
*
@@ -19699,11 +19903,13 @@
*
* @param {number|string} number Number to format.
* @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 `fractionSize` appropriately formatted based on the current
+ * locale (e.g., in the en_US locale it will have "." as the decimal separator and
+ * include "," group separators after each third digit).
*
* @example
<example module="numberFilterExample">
<file name="index.html">
<script>
@@ -23928,11 +24134,15 @@
} else if (!equals(newVal,oldVal)) {
var oldClasses = arrayClasses(oldVal);
updateClasses(oldClasses, newClasses);
}
}
- oldVal = shallowCopy(newVal);
+ if (isArray(newVal)) {
+ oldVal = newVal.map(function(v) { return shallowCopy(v); });
+ } else {
+ oldVal = shallowCopy(newVal);
+ }
}
}
};
function arrayDifference(tokens1, tokens2) {
@@ -25644,11 +25854,11 @@
if (toString.call($element[0]).match(/SVG/)) {
// WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
// support innerHTML, so detect this here and try to generate the contents
// specially.
$element.empty();
- $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
+ $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
function namespaceAdaptedClone(clone) {
$element.append(clone);
}, {futureParentElement: $element});
return;
}
@@ -27558,11 +27768,11 @@
// 8: collection expression
// 9: track by expression
// jshint maxlen: 100
-var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
+var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
function parseOptionsExpression(optionsExp, selectElement, scope) {
var match = optionsExp.match(NG_OPTIONS_REGEXP);
if (!(match)) {
@@ -27719,12 +27929,12 @@
}
// we can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
- var optionTemplate = document.createElement('option'),
- optGroupTemplate = document.createElement('optgroup');
+ var optionTemplate = window.document.createElement('option'),
+ optGroupTemplate = window.document.createElement('optgroup');
function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
var selectCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];
@@ -27745,12 +27955,15 @@
var unknownOption = jqLite(optionTemplate.cloneNode(false));
unknownOption.val('?');
var options;
var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
+ // This stores the newly created options before they are appended to the select.
+ // Since the contents are removed from the fragment when it is appended,
+ // we only need to create it once.
+ var listFragment = $document[0].createDocumentFragment();
-
var renderEmptyOption = function() {
if (!providedEmptyOption) {
selectElement.prepend(emptyOption);
}
selectElement.val('');
@@ -27780,11 +27993,11 @@
if (!multiple) {
selectCtrl.writeValue = function writeNgOptionsValue(value) {
var option = options.getOptionFromViewValue(value);
- if (option && !option.disabled) {
+ if (option) {
// 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
@@ -27842,11 +28055,11 @@
});
if (value) {
value.forEach(function(item) {
var option = options.getOptionFromViewValue(item);
- if (option && !option.disabled) option.element.selected = true;
+ if (option) option.element.selected = true;
});
}
};
@@ -27894,20 +28107,28 @@
emptyOption.removeClass('ng-scope');
} else {
emptyOption = jqLite(optionTemplate.cloneNode(false));
}
+ selectElement.empty();
+
// We need to do this here to ensure that the options object is defined
// when we first hit it in writeNgOptionsValue
updateOptions();
// We will re-render the option elements if the option values or labels change
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
// ------------------------------------------------------------------ //
+ function addOptionElement(option, parent) {
+ var optionElement = optionTemplate.cloneNode(false);
+ parent.appendChild(optionElement);
+ updateOptionElement(option, optionElement);
+ }
+
function updateOptionElement(option, element) {
option.element = element;
element.disabled = option.disabled;
// NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
// selects in certain circumstances when multiple selects are next to each other and display
@@ -27919,138 +28140,71 @@
element.textContent = option.label;
}
if (option.value !== element.value) element.value = option.selectValue;
}
- function addOrReuseElement(parent, current, type, templateElement) {
- var element;
- // Check whether we can reuse the next element
- if (current && lowercase(current.nodeName) === type) {
- // The next element is the right type so reuse it
- element = current;
- } else {
- // The next element is not the right type so create a new one
- element = templateElement.cloneNode(false);
- if (!current) {
- // There are no more elements so just append it to the select
- parent.appendChild(element);
- } else {
- // The next element is not a group so insert the new one
- parent.insertBefore(element, current);
- }
- }
- return element;
- }
+ function updateOptions() {
+ var previousValue = options && selectCtrl.readValue();
+ // We must remove all current options, but cannot simply set innerHTML = null
+ // since the providedEmptyOption might have an ngIf on it that inserts comments which we
+ // must preserve.
+ // Instead, iterate over the current option elements and remove them or their optgroup
+ // parents
+ if (options) {
- function removeExcessElements(current) {
- var next;
- while (current) {
- next = current.nextSibling;
- jqLiteRemove(current);
- current = next;
- }
- }
-
-
- function skipEmptyAndUnknownOptions(current) {
- var emptyOption_ = emptyOption && emptyOption[0];
- var unknownOption_ = unknownOption && unknownOption[0];
-
- // We cannot rely on the extracted empty option being the same as the compiled empty option,
- // because the compiled empty option might have been replaced by a comment because
- // it had an "element" transclusion directive on it (such as ngIf)
- if (emptyOption_ || unknownOption_) {
- while (current &&
- (current === emptyOption_ ||
- current === unknownOption_ ||
- current.nodeType === NODE_TYPE_COMMENT ||
- (nodeName_(current) === 'option' && current.value === ''))) {
- current = current.nextSibling;
+ for (var i = options.items.length - 1; i >= 0; i--) {
+ var option = options.items[i];
+ if (option.group) {
+ jqLiteRemove(option.element.parentNode);
+ } else {
+ jqLiteRemove(option.element);
+ }
}
}
- return current;
- }
-
- function updateOptions() {
-
- var previousValue = options && selectCtrl.readValue();
-
options = ngOptions.getOptions();
- var groupMap = {};
- var currentElement = selectElement[0].firstChild;
+ var groupElementMap = {};
// Ensure that the empty option is always there if it was explicitly provided
if (providedEmptyOption) {
selectElement.prepend(emptyOption);
}
- currentElement = skipEmptyAndUnknownOptions(currentElement);
-
- options.items.forEach(function updateOption(option) {
- var group;
+ options.items.forEach(function addOption(option) {
var groupElement;
- var optionElement;
if (isDefined(option.group)) {
// This option is to live in a group
// See if we have already created this group
- group = groupMap[option.group];
+ groupElement = groupElementMap[option.group];
- if (!group) {
+ if (!groupElement) {
- // We have not already created this group
- groupElement = addOrReuseElement(selectElement[0],
- currentElement,
- 'optgroup',
- optGroupTemplate);
- // Move to the next element
- currentElement = groupElement.nextSibling;
+ groupElement = optGroupTemplate.cloneNode(false);
+ listFragment.appendChild(groupElement);
// Update the label on the group element
groupElement.label = option.group;
// Store it for use later
- group = groupMap[option.group] = {
- groupElement: groupElement,
- currentOptionElement: groupElement.firstChild
- };
-
+ groupElementMap[option.group] = groupElement;
}
- // So now we have a group for this option we add the option to the group
- optionElement = addOrReuseElement(group.groupElement,
- group.currentOptionElement,
- 'option',
- optionTemplate);
- updateOptionElement(option, optionElement);
- // Move to the next element
- group.currentOptionElement = optionElement.nextSibling;
+ addOptionElement(option, groupElement);
} else {
// This option is not in a group
- optionElement = addOrReuseElement(selectElement[0],
- currentElement,
- 'option',
- optionTemplate);
- updateOptionElement(option, optionElement);
- // Move to the next element
- currentElement = optionElement.nextSibling;
+ addOptionElement(option, listFragment);
}
});
+ selectElement[0].appendChild(listFragment);
- // Now remove all excess options and group
- Object.keys(groupMap).forEach(function(key) {
- removeExcessElements(groupMap[key].currentOptionElement);
- });
- removeExcessElements(currentElement);
-
ngModelCtrl.$render();
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
@@ -29740,11 +29894,11 @@
// does not match any of the options. When it is rendered the value of the unknown
// option is '? XXX ?' where XXX is the hashKey of the value that is not known.
//
// We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
- self.unknownOption = jqLite(document.createElement('option'));
+ self.unknownOption = jqLite(window.document.createElement('option'));
self.renderUnknownOption = function(val) {
var unknownVal = '? ' + hashKey(val) + ' ?';
self.unknownOption.val(unknownVal);
$element.prepend(self.unknownOption);
$element.val(unknownVal);
@@ -30703,12 +30857,12 @@
"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;}
});
}]);
- jqLite(document).ready(function() {
- angularInit(document, bootstrap);
+ jqLite(window.document).ready(function() {
+ angularInit(window.document, bootstrap);
});
-})(window, document);
+})(window);
!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
\ No newline at end of file