vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.3.4 vs vendor/assets/javascripts/angular-scenario.js in angularjs-rails-1.3.6
- old
+ new
@@ -9188,11 +9188,11 @@
return jQuery;
}));
/**
- * @license AngularJS v1.3.4
+ * @license AngularJS v1.3.36
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, document){
var _jQuery = window.jQuery.noConflict(true);
@@ -9244,11 +9244,11 @@
return toDebugString(templateArgs[index + 2]);
}
return match;
});
- message = message + '\nhttp://errors.angularjs.org/1.3.4/' +
+ message = message + '\nhttp://errors.angularjs.org/1.3.36/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
encodeURIComponent(toDebugString(arguments[i]));
}
@@ -9416,12 +9416,12 @@
lowercase = manualLowercase;
uppercase = manualUppercase;
}
-var /** holds major version number for IE or NaN for real browsers */
- msie,
+var
+ msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
splice = [].splice,
push = [].push,
@@ -10218,16 +10218,20 @@
* @description
* Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
* stripped since angular uses this notation internally.
*
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
- * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
+ * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.
+ * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).
* @returns {string|undefined} JSON-ified string representing `obj`.
*/
function toJson(obj, pretty) {
if (typeof obj === 'undefined') return undefined;
- return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
+ if (!isNumber(pretty)) {
+ pretty = pretty ? 2 : null;
+ }
+ return JSON.stringify(obj, toJsonReplacer, pretty);
}
/**
* @ngdoc function
@@ -11271,11 +11275,12 @@
$TemplateRequestProvider,
$$TestabilityProvider,
$TimeoutProvider,
$$RAFProvider,
$$AsyncCallbackProvider,
- $WindowProvider
+ $WindowProvider,
+ $$jqLiteProvider
*/
/**
* @ngdoc object
@@ -11290,15 +11295,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.3.4', // all of these placeholder strings will be replaced by grunt's
+ full: '1.3.36', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 3,
- dot: 4,
- codeName: 'highfalutin-petroglyph'
+ dot: 36,
+ codeName: 'robofunky-danceblaster'
};
function publishExternalAPI(angular) {
extend(angular, {
@@ -11424,11 +11429,12 @@
$templateRequest: $TemplateRequestProvider,
$$testability: $$TestabilityProvider,
$timeout: $TimeoutProvider,
$window: $WindowProvider,
$$rAF: $$RAFProvider,
- $$asyncCallback: $$AsyncCallbackProvider
+ $$asyncCallback: $$AsyncCallbackProvider,
+ $$jqLite: $$jqLiteProvider
});
}
]);
}
@@ -12434,10 +12440,31 @@
// bind legacy bind/unbind to on/off
JQLite.prototype.bind = JQLite.prototype.on;
JQLite.prototype.unbind = JQLite.prototype.off;
});
+
+// Provider for private $$jqLite service
+function $$jqLiteProvider() {
+ this.$get = function $$jqLite() {
+ return extend(JQLite, {
+ hasClass: function(node, classes) {
+ if (node.attr) node = node[0];
+ return jqLiteHasClass(node, classes);
+ },
+ addClass: function(node, classes) {
+ if (node.attr) node = node[0];
+ return jqLiteAddClass(node, classes);
+ },
+ removeClass: function(node, classes) {
+ if (node.attr) node = node[0];
+ return jqLiteRemoveClass(node, classes);
+ }
+ });
+ };
+}
+
/**
* Computes a hash of an 'obj'.
* Hash of a:
* string is string
* number is number as string
@@ -12686,10 +12713,11 @@
*
* @description
* Return an instance of the service.
*
* @param {string} name The name of the instance to retrieve.
+ * @param {string} caller An optional string to provide the origin of the function call for error messages.
* @return {*} The instance.
*/
/**
* @ngdoc method
@@ -13136,18 +13164,21 @@
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
- createInternalInjector(providerCache, function() {
+ createInternalInjector(providerCache, function(serviceName, caller) {
+ if (angular.isString(caller)) {
+ path.push(caller);
+ }
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
- createInternalInjector(instanceCache, function(servicename) {
- var provider = providerInjector.get(servicename + providerSuffix);
- return instanceInjector.invoke(provider.$get, provider, undefined, servicename);
+ createInternalInjector(instanceCache, function(serviceName, caller) {
+ var provider = providerInjector.get(serviceName + providerSuffix, caller);
+ return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
@@ -13178,11 +13209,11 @@
return providerCache[name + providerSuffix] = provider_;
}
function enforceReturnValue(name, factory) {
return function enforcedReturnValue() {
- var result = instanceInjector.invoke(factory, this, undefined, name);
+ var result = instanceInjector.invoke(factory, this);
if (isUndefined(result)) {
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
}
return result;
};
@@ -13273,22 +13304,22 @@
// internal Injector
////////////////////////////////////
function createInternalInjector(cache, factory) {
- function getService(serviceName) {
+ function getService(serviceName, caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
serviceName + ' <- ' + path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
- return cache[serviceName] = factory(serviceName);
+ return cache[serviceName] = factory(serviceName, caller);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
@@ -13316,11 +13347,11 @@
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
- : getService(key)
+ : getService(key, serviceName)
);
}
if (isArray(fn)) {
fn = fn[length];
}
@@ -14060,10 +14091,15 @@
}
}
}
}
+ function getHash(url) {
+ var index = url.indexOf('#');
+ return index === -1 ? '' : url.substr(index + 1);
+ }
+
/**
* @private
* Note: this method is used only by scenario runner
* TODO(vojta): prefix this method with $$ ?
* @param {function()} callback Function that will be called when no outstanding request
@@ -14189,12 +14225,14 @@
if (!sameBase) {
reloadLocation = url;
}
if (replace) {
location.replace(url);
- } else {
+ } else if (!sameBase) {
location.href = url;
+ } else {
+ location.hash = getHash(url);
}
}
return self;
// getter
} else {
@@ -14969,11 +15007,11 @@
* compiler}. The attributes are:
*
* #### `multiElement`
* When this property is set to true, the HTML compiler will collect DOM nodes between
* nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
- * together as the directive elements. It is recomended that this feature be used on directives
+ * together as the directive elements. It is recommended that this feature be used on directives
* which are not strictly behavioural (such as {@link ngClick}), and which
* do not manipulate or replace child nodes (such as {@link ngInclude}).
*
* #### `priority`
* When there are multiple directives defined on a single DOM element, sometimes it
@@ -15657,11 +15695,11 @@
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
- * The sanitization is a security measure aimed at prevent XSS attacks via html links.
+ * The sanitization is a security measure aimed at preventing XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
@@ -15724,11 +15762,11 @@
* binding information and a reference to the current scope on to DOM elements.
* If enabled, the compiler will add the following to DOM elements that have been bound to the scope
* * `ng-binding` CSS class
* * `$binding` data property containing an array of the binding expressions
*
- * You may want to use this in production for a significant performance boost. See
+ * You may want to disable this in production for a significant performance boost. See
* {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
*
* The default value is true.
*/
var debugInfoEnabled = true;
@@ -15761,10 +15799,25 @@
this.$$element = element;
};
Attributes.prototype = {
+ /**
+ * @ngdoc method
+ * @name $compile.directive.Attributes#$normalize
+ * @kind function
+ *
+ * @description
+ * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
+ * `data-`) to its normalized, camelCase form.
+ *
+ * Also there is special case for Moz prefix starting with upper case letter.
+ *
+ * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
+ *
+ * @param {string} name Name to normalize
+ */
$normalize: directiveNormalize,
/**
* @ngdoc method
@@ -17339,17 +17392,10 @@
}
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
/**
* Converts all accepted directives format into proper directive name.
- * All of these will become 'myDirective':
- * my:Directive
- * my-directive
- * x-my-directive
- * data-my:directive
- *
- * Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize
*/
function directiveNormalize(name) {
return camelCase(name.replace(PREFIX_REGEXP, ''));
}
@@ -18281,16 +18327,18 @@
* - **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
* request body and headers and returns its transformed (typically serialized) version.
- * See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations}
+ * See {@link ng.$http#overriding-the-default-transformations-per-request
+ * Overriding the Default Transformations}
* - **transformResponse** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
- * See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations}
+ * See {@link ng.$http#overriding-the-default-transformations-per-request
+ * Overriding the Default Transformations}
* - **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}
@@ -18696,12 +18744,11 @@
if (cache) {
cachedResp = cache.get(url);
if (isDefined(cachedResp)) {
if (isPromiseLike(cachedResp)) {
// cached request has already been sent, but there is no response yet
- cachedResp.then(removePendingReq, removePendingReq);
- return cachedResp;
+ cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
} else {
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
} else {
@@ -18775,10 +18822,13 @@
config: config,
statusText: statusText
});
}
+ function resolvePromiseWithResult(result) {
+ resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
+ }
function removePendingReq() {
var idx = $http.pendingRequests.indexOf(config);
if (idx !== -1) $http.pendingRequests.splice(idx, 1);
}
@@ -18936,11 +18986,13 @@
xhr && xhr.abort();
}
function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
- timeoutId && $browserDefer.cancel(timeoutId);
+ if (timeoutId !== undefined) {
+ $browserDefer.cancel(timeoutId);
+ }
jsonpDone = xhr = null;
callback(status, response, headersString, statusText);
$browser.$$completeOutstandingRequest(noop);
}
@@ -19384,37 +19436,37 @@
* var stop;
* $scope.fight = function() {
* // Don't start a new fight if we are already fighting
* if ( angular.isDefined(stop) ) return;
*
- * stop = $interval(function() {
- * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
- * $scope.blood_1 = $scope.blood_1 - 3;
- * $scope.blood_2 = $scope.blood_2 - 4;
- * } else {
- * $scope.stopFight();
+ * stop = $interval(function() {
+ * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
+ * $scope.blood_1 = $scope.blood_1 - 3;
+ * $scope.blood_2 = $scope.blood_2 - 4;
+ * } else {
+ * $scope.stopFight();
+ * }
+ * }, 100);
+ * };
+ *
+ * $scope.stopFight = function() {
+ * if (angular.isDefined(stop)) {
+ * $interval.cancel(stop);
+ * stop = undefined;
* }
- * }, 100);
- * };
+ * };
*
- * $scope.stopFight = function() {
- * if (angular.isDefined(stop)) {
- * $interval.cancel(stop);
- * stop = undefined;
- * }
- * };
+ * $scope.resetFight = function() {
+ * $scope.blood_1 = 100;
+ * $scope.blood_2 = 120;
+ * };
*
- * $scope.resetFight = function() {
- * $scope.blood_1 = 100;
- * $scope.blood_2 = 120;
- * };
- *
- * $scope.$on('$destroy', function() {
- * // Make sure that the interval is destroyed too
- * $scope.stopFight();
- * });
- * }])
+ * $scope.$on('$destroy', function() {
+ * // Make sure that the interval is destroyed too
+ * $scope.stopFight();
+ * });
+ * }])
* // Register the 'myCurrentTime' directive factory method.
* // We inject $interval and dateFilter service since the factory method is DI.
* .directive('myCurrentTime', ['$interval', 'dateFilter',
* function($interval, dateFilter) {
* // return the directive link function. (compile function not needed)
@@ -19653,11 +19705,15 @@
function stripHash(url) {
var index = url.indexOf('#');
return index == -1 ? url : url.substr(0, index);
}
+function trimEmptyHash(url) {
+ return url.replace(/(#.+)|#$/, '$1');
+}
+
function stripFile(url) {
return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
}
/* return the server only (scheme://host:port) */
@@ -19764,20 +19820,29 @@
* @param {string} url Hashbang url
* @private
*/
this.$$parse = function(url) {
var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
- var withoutHashUrl = withoutBaseUrl.charAt(0) == '#'
- ? beginsWith(hashPrefix, withoutBaseUrl)
- : (this.$$html5)
- ? withoutBaseUrl
- : '';
+ var withoutHashUrl;
- if (!isString(withoutHashUrl)) {
- throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
- hashPrefix);
+ if (withoutBaseUrl.charAt(0) === '#') {
+
+ // The rest of the url starts with a hash so we have
+ // got either a hashbang path or a plain hash fragment
+ withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
+ if (isUndefined(withoutHashUrl)) {
+ // There was no hashbang prefix so we just have a hash fragment
+ withoutHashUrl = withoutBaseUrl;
+ }
+
+ } else {
+ // There was no hashbang path nor hash fragment:
+ // If we are in HTML5 mode we use what is left as the path;
+ // Otherwise we ignore what is left
+ withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
}
+
parseAppUrl(withoutHashUrl, this);
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
this.$$compose();
@@ -20136,11 +20201,11 @@
*
* Change hash fragment when called with parameter and return `$location`.
*
*
* ```js
- * // given url http://example.com/some/path?foo=bar&baz=xoxo#hashValue
+ * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
* var hash = $location.hash();
* // => "hashValue"
* ```
*
* @param {(string|number)=} hash New hash fragment
@@ -20488,14 +20553,15 @@
if (!$rootScope.$$phase) $rootScope.$digest();
});
// update browser
$rootScope.$watch(function $locationWatch() {
- var oldUrl = $browser.url();
+ var oldUrl = trimEmptyHash($browser.url());
+ var newUrl = trimEmptyHash($location.absUrl());
var oldState = $browser.state();
var currentReplace = $location.$$replace;
- var urlOrStateChanged = oldUrl !== $location.absUrl() ||
+ var urlOrStateChanged = oldUrl !== newUrl ||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
if (initializing || urlOrStateChanged) {
initializing = false;
@@ -21294,30 +21360,30 @@
},
logicalAND: function() {
var left = this.equality();
var token;
- if ((token = this.expect('&&'))) {
- left = this.binaryFn(left, token.text, this.logicalAND(), true);
+ while ((token = this.expect('&&'))) {
+ left = this.binaryFn(left, token.text, this.equality(), true);
}
return left;
},
equality: function() {
var left = this.relational();
var token;
- if ((token = this.expect('==','!=','===','!=='))) {
- left = this.binaryFn(left, token.text, this.equality());
+ while ((token = this.expect('==','!=','===','!=='))) {
+ left = this.binaryFn(left, token.text, this.relational());
}
return left;
},
relational: function() {
var left = this.additive();
var token;
- if ((token = this.expect('<', '>', '<=', '>='))) {
- left = this.binaryFn(left, token.text, this.relational());
+ while ((token = this.expect('<', '>', '<=', '>='))) {
+ left = this.binaryFn(left, token.text, this.additive());
}
return left;
},
additive: function() {
@@ -21405,11 +21471,11 @@
var expressionText = this.text;
// we can safely reuse the array across invocations
var args = argsFn.length ? [] : null;
return function $parseFunctionCall(scope, locals) {
- var context = contextGetter ? contextGetter(scope, locals) : scope;
+ var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
var fn = fnGetter(scope, locals, context) || noop;
if (args) {
var i = argsFn.length;
while (i--) {
@@ -21418,17 +21484,17 @@
}
ensureSafeObject(context, expressionText);
ensureSafeFunction(fn, expressionText);
- // IE stupidity! (IE doesn't have apply for some native functions)
+ // IE doesn't have apply for some native functions
var v = fn.apply
? fn.apply(context, args)
: fn(args[0], args[1], args[2], args[3], args[4]);
return ensureSafeObject(v, expressionText);
- };
+ };
},
// This is used with json array declaration
arrayDeclaration: function() {
var elementFns = [];
@@ -25060,11 +25126,13 @@
// jshint +W018
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
// it. In particular the event is not fired when backspace or delete key are pressed or
// when cut operation is performed.
- if (event == 'input' && msie == 9) return false;
+ // IE10+ implements 'input' event but it erroneously fires under various situations,
+ // e.g. when placeholder changes, or a form is focused.
+ if (event === 'input' && msie <= 11) return false;
if (isUndefined(eventSupport[event])) {
var divElm = document.createElement('div');
eventSupport[event] = 'on' + event in divElm;
}
@@ -25106,18 +25174,13 @@
self.totalPendingRequests++;
var transformResponse = $http.defaults && $http.defaults.transformResponse;
if (isArray(transformResponse)) {
- var original = transformResponse;
- transformResponse = [];
- for (var i = 0; i < original.length; ++i) {
- var transformer = original[i];
- if (transformer !== defaultHttpResponseTransform) {
- transformResponse.push(transformer);
- }
- }
+ transformResponse = transformResponse.filter(function(transformer) {
+ return transformer !== defaultHttpResponseTransform;
+ });
} else if (transformResponse === defaultHttpResponseTransform) {
transformResponse = null;
}
var httpOptions = {
@@ -25125,22 +25188,20 @@
transformResponse: transformResponse
};
return $http.get(tpl, httpOptions)
.then(function(response) {
- var html = response.data;
self.totalPendingRequests--;
- $templateCache.put(tpl, html);
- return html;
+ return response.data;
}, handleError);
- function handleError() {
+ function handleError(resp) {
self.totalPendingRequests--;
if (!ignoreRequestError) {
throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
}
- return $q.reject();
+ return $q.reject(resp);
}
}
handleRequestFn.totalPendingRequests = 0;
@@ -25759,112 +25820,109 @@
*/
function filterFilter() {
return function(array, expression, comparator) {
if (!isArray(array)) return array;
- var comparatorType = typeof(comparator),
- predicates = [];
+ var predicateFn;
+ var matchAgainstAnyProp;
- predicates.check = function(value, index) {
- for (var j = 0; j < predicates.length; j++) {
- if (!predicates[j](value, index)) {
- return false;
- }
- }
- return true;
- };
-
- if (comparatorType !== 'function') {
- if (comparatorType === 'boolean' && comparator) {
- comparator = function(obj, text) {
- return angular.equals(obj, text);
- };
- } else {
- comparator = function(obj, text) {
- if (obj && text && typeof obj === 'object' && typeof text === 'object') {
- for (var objKey in obj) {
- if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
- comparator(obj[objKey], text[objKey])) {
- return true;
- }
- }
- return false;
- }
- text = ('' + text).toLowerCase();
- return ('' + obj).toLowerCase().indexOf(text) > -1;
- };
- }
- }
-
- var search = function(obj, text) {
- if (typeof text === 'string' && text.charAt(0) === '!') {
- return !search(obj, text.substr(1));
- }
- switch (typeof obj) {
- case 'boolean':
- case 'number':
- case 'string':
- return comparator(obj, text);
- case 'object':
- switch (typeof text) {
- case 'object':
- return comparator(obj, text);
- default:
- for (var objKey in obj) {
- if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
- return true;
- }
- }
- break;
- }
- return false;
- case 'array':
- for (var i = 0; i < obj.length; i++) {
- if (search(obj[i], text)) {
- return true;
- }
- }
- return false;
- default:
- return false;
- }
- };
switch (typeof expression) {
+ case 'function':
+ predicateFn = expression;
+ break;
case 'boolean':
case 'number':
case 'string':
- // Set up expression object and fall through
- expression = {$:expression};
- // jshint -W086
+ matchAgainstAnyProp = true;
+ //jshint -W086
case 'object':
- // jshint +W086
- for (var key in expression) {
- (function(path) {
- if (typeof expression[path] === 'undefined') return;
- predicates.push(function(value) {
- return search(path == '$' ? value : (value && value[path]), expression[path]);
- });
- })(key);
- }
+ //jshint +W086
+ predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
break;
- case 'function':
- predicates.push(expression);
- break;
default:
return array;
}
- var filtered = [];
- for (var j = 0; j < array.length; j++) {
- var value = array[j];
- if (predicates.check(value, j)) {
- filtered.push(value);
+
+ return array.filter(predicateFn);
+ };
+}
+
+// Helper functions for `filterFilter`
+function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
+ var predicateFn;
+
+ if (comparator === true) {
+ comparator = equals;
+ } else if (!isFunction(comparator)) {
+ comparator = function(actual, expected) {
+ if (isObject(actual) || isObject(expected)) {
+ // Prevent an object to be considered equal to a string like `'[object'`
+ return false;
}
- }
- return filtered;
+
+ actual = lowercase('' + actual);
+ expected = lowercase('' + expected);
+ return actual.indexOf(expected) !== -1;
+ };
+ }
+
+ predicateFn = function(item) {
+ return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
+
+ return predicateFn;
}
+function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
+ var actualType = typeof actual;
+ var expectedType = typeof expected;
+
+ if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
+ return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
+ } else if (actualType === 'array') {
+ // In case `actual` is an array, consider it a match
+ // if ANY of it's items matches `expected`
+ return actual.some(function(item) {
+ return deepCompare(item, expected, comparator, matchAgainstAnyProp);
+ });
+ }
+
+ switch (actualType) {
+ case 'object':
+ var key;
+ if (matchAgainstAnyProp) {
+ for (key in actual) {
+ if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) {
+ return true;
+ }
+ }
+ return false;
+ } else if (expectedType === 'object') {
+ for (key in expected) {
+ var expectedVal = expected[key];
+ if (isFunction(expectedVal)) {
+ continue;
+ }
+
+ var keyIsDollar = key === '$';
+ var actualVal = keyIsDollar ? actual : actual[key];
+ if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return comparator(actual, expected);
+ }
+ break;
+ case 'function':
+ return false;
+ default:
+ return comparator(actual, expected);
+ }
+}
+
/**
* @ngdoc filter
* @name currency
* @kind function
*
@@ -26011,11 +26069,10 @@
var hasExponent = false;
if (numStr.indexOf('e') !== -1) {
var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
if (match && match[2] == '-' && match[3] > fractionSize + 1) {
- numStr = '0';
number = 0;
} else {
formatedText = numStr;
hasExponent = true;
}
@@ -26032,14 +26089,10 @@
// 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);
- if (number === 0) {
- isNegative = false;
- }
-
var fraction = ('' + number).split(DECIMAL_SEP);
var whole = fraction[0];
fraction = fraction[1] || '';
var i, pos = 0,
@@ -26068,16 +26121,20 @@
fraction += '0';
}
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
} else {
-
- if (fractionSize > 0 && number > -1 && number < 1) {
+ if (fractionSize > 0 && number < 1) {
formatedText = number.toFixed(fractionSize);
+ number = parseFloat(formatedText);
}
}
+ if (number === 0) {
+ isNegative = false;
+ }
+
parts.push(isNegative ? pattern.negPre : pattern.posPre,
formatedText,
isNegative ? pattern.negSuf : pattern.posSuf);
return parts.join('');
}
@@ -26363,29 +26420,35 @@
*
* This filter is mostly useful for debugging. When using the double curly {{value}} notation
* the binding is automatically converted to JSON.
*
* @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
+ * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
* @returns {string} JSON string.
*
*
* @example
<example>
<file name="index.html">
- <pre>{{ {'name':'value'} | json }}</pre>
+ <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
+ <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
</file>
<file name="protractor.js" type="protractor">
it('should jsonify filtered objects', function() {
- expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/);
+ expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
+ expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
});
</file>
</example>
*
*/
function jsonFilter() {
- return function(object) {
- return toJson(object, true);
+ return function(object, spacing) {
+ if (isUndefined(spacing)) {
+ spacing = 2;
+ }
+ return toJson(object, spacing);
};
}
/**
@@ -26701,16 +26764,33 @@
: comp;
}
function compare(v1, v2) {
var t1 = typeof v1;
var t2 = typeof v2;
- if (t1 == t2) {
- if (isDate(v1) && isDate(v2)) {
- v1 = v1.valueOf();
- v2 = v2.valueOf();
+ // Prepare values for Abstract Relational Comparison
+ // (http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5):
+ // If the resulting values are identical, return 0 to prevent
+ // incorrect re-ordering.
+ if (t1 === t2 && t1 === "object") {
+ // If types are both numbers, emulate abstract ToPrimitive() operation
+ // in order to get primitive values suitable for comparison
+ t1 = typeof (v1.valueOf ? v1 = v1.valueOf() : v1);
+ t2 = typeof (v2.valueOf ? v2 = v2.valueOf() : v2);
+ if (t1 === t2 && t1 === "object") {
+ // Object.prototype.valueOf will return the original object, by
+ // default. If we do not receive a primitive value, use ToString()
+ // instead.
+ t1 = typeof (v1.toString ? v1 = v1.toString() : v1);
+ t2 = typeof (v2.toString ? v2 = v2.toString() : v2);
+
+ // If the end result of toString() for each item is the same, do not
+ // perform relational comparison, and do not re-order objects.
+ if (t1 === t2 && v1 === v2 || t1 === "object") return 0;
}
- if (t1 == "string") {
+ }
+ if (t1 === t2) {
+ if (t1 === "string") {
v1 = v1.toLowerCase();
v2 = v2.toLowerCase();
}
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
@@ -26772,14 +26852,13 @@
* @description
* Using Angular markup like `{{hash}}` in an href attribute will
* make the link go to the wrong URL if the user clicks it before
* Angular has a chance to replace the `{{hash}}` markup with its
* value. Until Angular replaces the markup the link will be broken
- * and will most likely return a 404 error.
+ * and will most likely return a 404 error. The `ngHref` directive
+ * solves this problem.
*
- * The `ngHref` directive solves this problem.
- *
* The wrong way to write it:
* ```html
* <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
* ```
*
@@ -28656,11 +28735,10 @@
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
stringBasedInputType(ctrl);
}
function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
- var placeholder = element[0].placeholder, noevent = {};
var type = lowercase(element[0].type);
// In composition mode, users are still inputing intermediate text buffer,
// hold the listener until composition is done.
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
@@ -28676,23 +28754,18 @@
listener();
});
}
var listener = function(ev) {
+ if (timeout) {
+ $browser.defer.cancel(timeout);
+ timeout = null;
+ }
if (composing) return;
var value = element.val(),
event = ev && ev.type;
- // IE (11 and under) seem to emit an 'input' event if the placeholder value changes.
- // We don't want to dirty the value when this happens, so we abort here. Unfortunately,
- // IE also sends input events for other non-input-related things, (such as focusing on a
- // form control), so this change is not entirely enough to solve this.
- if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) {
- placeholder = element[0].placeholder;
- return;
- }
-
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// If input type is 'password', the value is never trimmed
if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
value = trim(value);
@@ -28711,15 +28784,17 @@
if ($sniffer.hasEvent('input')) {
element.on('input', listener);
} else {
var timeout;
- var deferListener = function(ev) {
+ var deferListener = function(ev, input, origValue) {
if (!timeout) {
timeout = $browser.defer(function() {
- listener(ev);
timeout = null;
+ if (!input || input.value !== origValue) {
+ listener(ev);
+ }
});
}
};
element.on('keydown', function(event) {
@@ -28727,11 +28802,11 @@
// ignore
// command modifiers arrows
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
- deferListener(event);
+ deferListener(event, this, this.value);
});
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
if ($sniffer.hasEvent('paste')) {
element.on('paste cut', deferListener);
@@ -29902,15 +29977,19 @@
ctrl.$modelValue = ngModelGet($scope);
}
var prevModelValue = ctrl.$modelValue;
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
ctrl.$$rawModelValue = modelValue;
+
if (allowInvalid) {
ctrl.$modelValue = modelValue;
writeToModelIfNeeded();
}
- ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
+
+ // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
+ // This can happen if e.g. $setViewValue is called from inside a parser
+ ctrl.$$runValidators(parserValid, modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
if (!allowInvalid) {
// Note: Don't check ctrl.$valid here, as we could have
// external validators (e.g. calculated on the server),
// that just call $setValidity and need the model value
// to calculate their validity.
@@ -34248,11 +34327,11 @@
$scope.title = 'Lorem Ipsum';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);
</script>
<div ng-controller="ExampleController">
- <input ng-model="title"><br>
+ <input ng-model="title"> <br/>
<textarea ng-model="text"></textarea> <br/>
<pane title="{{title}}">{{text}}</pane>
</div>
</file>
<file name="protractor.js" type="protractor">
@@ -34326,11 +34405,10 @@
restrict: 'E',
terminal: true,
compile: function(element, attr) {
if (attr.type == 'text/ng-template') {
var templateUrl = attr.id,
- // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
text = element[0].text;
$templateCache.put(templateUrl, text);
}
}
@@ -34377,13 +34455,13 @@
* Using `select as` will bind the result of the `select as` expression to the model, but
* the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
* or property name (for object data sources) of the value within the collection. If a `track by` expression
* is used, the result of that expression will be set as the value of the `option` and `select` elements.
*
- * ### `select as` with `trackexpr`
+ * ### `select as` with `track by`
*
- * Using `select as` together with `trackexpr` is not recommended. Reasoning:
+ * Using `select as` together with `track by` is not recommended. Reasoning:
*
* - Example: <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
* values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}],
* $scope.selected = {name: 'aSubItem'};
* - track by is always applied to `value`, with the purpose of preserving the selection,
@@ -34404,12 +34482,14 @@
* @param {comprehension_expression=} ngOptions in one of the following forms:
*
* * for array data sources:
* * `label` **`for`** `value` **`in`** `array`
* * `select` **`as`** `label` **`for`** `value` **`in`** `array`
- * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
- * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
+ * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
+ * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
+ * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
+ * (for including a filter with `track by`)
* * for object data sources:
* * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
* * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
* * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
* * `select` **`as`** `label` **`group by`** `group`
@@ -34547,11 +34627,11 @@
self.removeOption = function(value) {
if (this.hasOption(value)) {
delete optionsMap[value];
- if (ngModelCtrl.$viewValue == value) {
+ if (ngModelCtrl.$viewValue === value) {
this.renderUnknownOption(value);
}
}
};
@@ -35014,22 +35094,27 @@
while (existingOptions.length > index) {
option = existingOptions.pop();
updateLabelMap(labelMap, option.label, false);
option.element.remove();
}
- forEach(labelMap, function(count, label) {
- if (count > 0) {
- selectCtrl.addOption(label);
- } else if (count < 0) {
- selectCtrl.removeOption(label);
- }
- });
}
// remove any excessive OPTGROUPs from select
while (optionGroupsCache.length > groupIndex) {
- optionGroupsCache.pop()[0].element.remove();
+ // remove all the labels in the option group
+ optionGroup = optionGroupsCache.pop();
+ for (index = 1; index < optionGroup.length; ++index) {
+ updateLabelMap(labelMap, optionGroup[index].label, false);
+ }
+ optionGroup[0].element.remove();
}
+ forEach(labelMap, function(count, label) {
+ if (count > 0) {
+ selectCtrl.addOption(label);
+ } else if (count < 0) {
+ selectCtrl.removeOption(label);
+ }
+ });
}
}
}
};
}];
@@ -36686,10 +36771,10 @@
* input(name).select(value) selects the radio button with specified name/value
* input(name).val() returns the value of the input.
*/
angular.scenario.dsl('input', function() {
var chain = {};
- var supportInputEvent = 'oninput' in document.createElement('div') && msie != 9;
+ var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
chain.enter = function(value, event) {
return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
function($window, $document, done) {
var input = $document.elements('[ng\\:model="$1"]', this.name).filter(':input');
\ No newline at end of file