vendor/assets/javascripts/angular-mocks.js in angularjs-rails-1.5.8 vs vendor/assets/javascripts/angular-mocks.js in angularjs-rails-1.6.0
- old
+ new
@@ -1,7 +1,7 @@
/**
- * @license AngularJS v1.5.8
+ * @license AngularJS v1.6.0
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {
@@ -38,11 +38,11 @@
angular.mock.$Browser = function() {
var self = this;
this.isMock = true;
- self.$$url = "http://server/";
+ self.$$url = 'http://server/';
self.$$lastUrl = self.$$url; // used by url polling fn
self.pollFns = [];
// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = angular.noop;
@@ -250,23 +250,23 @@
switch (mode) {
case 'log':
case 'rethrow':
var errors = [];
handler = function(e) {
- if (arguments.length == 1) {
+ if (arguments.length === 1) {
errors.push(e);
} else {
errors.push([].slice.call(arguments, 0));
}
- if (mode === "rethrow") {
+ if (mode === 'rethrow') {
throw e;
}
};
handler.errors = errors;
break;
default:
- throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
+ throw new Error('Unknown mode \'' + mode + '\', only \'log\'/\'rethrow\' modes are allowed!');
}
};
this.$get = function() {
return handler;
@@ -412,12 +412,12 @@
(logItem.stack || ''));
});
});
});
if (errors.length) {
- errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " +
- "an expected log message was not checked and removed:");
+ errors.unshift('Expected $log to be empty! Either a message was logged unexpectedly, or ' +
+ 'an expected log message was not checked and removed:');
errors.push('');
throw new Error(errors.join('\n---------\n'));
}
};
@@ -461,11 +461,11 @@
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = (angular.isDefined(count)) ? count : 0;
- promise.then(null, null, (!hasParams) ? fn : function() {
+ promise.then(null, function() {}, (!hasParams) ? fn : function() {
fn.apply(null, args);
});
promise.$$intervalId = nextRepeatId;
@@ -521,10 +521,11 @@
angular.forEach(repeatFns, function(fn, index) {
if (fn.id === promise.$$intervalId) fnIndex = index;
});
if (angular.isDefined(fnIndex)) {
+ repeatFns[fnIndex].deferred.promise.then(undefined, function() {});
repeatFns[fnIndex].deferred.reject('canceled');
repeatFns.splice(fnIndex, 1);
return true;
}
@@ -556,20 +557,17 @@
return $interval;
}];
};
-/* jshint -W101 */
-/* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
- * This directive should go inside the anonymous function but a bug in JSHint means that it would
- * not be enacted early enough to prevent the warning.
- */
-var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
-
function jsonStringToDate(string) {
+ // The R_ISO8061_STR regex is never going to fit into the 100 char limit!
+ // eslit-disable-next-line max-len
+ var R_ISO8061_STR = /^(-?\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
+
var match;
- if (match = string.match(R_ISO8061_STR)) {
+ if ((match = string.match(R_ISO8061_STR))) {
var date = new Date(0),
tzHour = 0,
tzMin = 0;
if (match[9]) {
tzHour = toInt(match[9] + match[10]);
@@ -648,13 +646,14 @@
self.origDate = jsonStringToDate(timestamp);
timestamp = self.origDate.getTime();
if (isNaN(timestamp)) {
+ // eslint-disable-next-line no-throw-literal
throw {
- name: "Illegal Argument",
- message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
+ name: 'Illegal Argument',
+ message: 'Arg \'' + tsStr + '\' passed into TzDate constructor is not a valid date string'
};
}
} else {
self.origDate = new Date(timestamp);
}
@@ -756,20 +755,19 @@
'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
angular.forEach(unimplementedMethods, function(methodName) {
self[methodName] = function() {
- throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
+ throw new Error('Method \'' + methodName + '\' is not implemented in the TzDate mock');
};
});
return self;
};
//make "tzDateInstance instanceof Date" return true
angular.mock.TzDate.prototype = Date.prototype;
-/* jshint +W101 */
/**
* @ngdoc service
* @name $animate
@@ -1213,11 +1211,11 @@
$httpBackend.flush();
$httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
// check if the header was sent, if it wasn't the expectation won't
// match the request and the test will fail
- return headers['Authorization'] == 'xxx';
+ return headers['Authorization'] === 'xxx';
}).respond(201, '');
$rootScope.saveMessage('whatever');
$httpBackend.flush();
});
@@ -1355,11 +1353,15 @@
: angular.toJson(data);
}
function wrapResponse(wrapped) {
if (!$browser && timeout) {
- timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout);
+ if (timeout.then) {
+ timeout.then(handleTimeout);
+ } else {
+ $timeout(handleTimeout, timeout);
+ }
}
return handleResponse;
function handleResponse() {
@@ -1424,11 +1426,11 @@
* @name $httpBackend#when
* @description
* Creates a new backend definition.
*
* @param {string} method HTTP method.
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current definition.
@@ -1446,10 +1448,13 @@
* return an array containing response status (number), response data (Array|Object|string),
* response headers (Object), and the text for the status (string). The respond method returns
* the `requestHandler` object for possible overrides.
*/
$httpBackend.when = function(method, url, data, headers, keys) {
+
+ assertArgDefined(arguments, 1, 'url');
+
var definition = new MockHttpExpectation(method, url, data, headers, keys),
chain = {
respond: function(status, data, headers, statusText) {
definition.passThrough = undefined;
definition.response = createResponse(status, data, headers, statusText);
@@ -1473,11 +1478,11 @@
* @ngdoc method
* @name $httpBackend#whenGET
* @description
* Creates a new backend definition for GET requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1488,11 +1493,11 @@
* @ngdoc method
* @name $httpBackend#whenHEAD
* @description
* Creates a new backend definition for HEAD requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1503,11 +1508,11 @@
* @ngdoc method
* @name $httpBackend#whenDELETE
* @description
* Creates a new backend definition for DELETE requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1518,11 +1523,11 @@
* @ngdoc method
* @name $httpBackend#whenPOST
* @description
* Creates a new backend definition for POST requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1535,11 +1540,11 @@
* @ngdoc method
* @name $httpBackend#whenPUT
* @description
* Creates a new backend definition for PUT requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
@@ -1552,11 +1557,11 @@
* @ngdoc method
* @name $httpBackend#whenJSONP
* @description
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
* order to change how a matched request is handled.
@@ -1588,11 +1593,11 @@
if (!url || !angular.isString(url)) return ret;
url = url
.replace(/([().])/g, '\\$1')
- .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
+ .replace(/(\/)?:(\w+)([?*])?/g, function(_, slash, key, option) {
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
slash = slash || '';
return ''
@@ -1602,11 +1607,11 @@
+ (star && '(.+?)' || '([^/]+)')
+ (optional || '')
+ ')'
+ (optional || '');
})
- .replace(/([\/$\*])/g, '\\$1');
+ .replace(/([/$*])/g, '\\$1');
ret.regexp = new RegExp('^' + url, 'i');
return ret;
}
@@ -1615,11 +1620,11 @@
* @name $httpBackend#expect
* @description
* Creates a new request expectation.
*
* @param {string} method HTTP method.
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
@@ -1638,10 +1643,13 @@
* return an array containing response status (number), response data (Array|Object|string),
* response headers (Object), and the text for the status (string). The respond method returns
* the `requestHandler` object for possible overrides.
*/
$httpBackend.expect = function(method, url, data, headers, keys) {
+
+ assertArgDefined(arguments, 1, 'url');
+
var expectation = new MockHttpExpectation(method, url, data, headers, keys),
chain = {
respond: function(status, data, headers, statusText) {
expectation.response = createResponse(status, data, headers, statusText);
return chain;
@@ -1656,11 +1664,11 @@
* @ngdoc method
* @name $httpBackend#expectGET
* @description
* Creates a new request expectation for GET requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1671,11 +1679,11 @@
* @ngdoc method
* @name $httpBackend#expectHEAD
* @description
* Creates a new request expectation for HEAD requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1686,11 +1694,11 @@
* @ngdoc method
* @name $httpBackend#expectDELETE
* @description
* Creates a new request expectation for DELETE requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {Object=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
@@ -1701,11 +1709,11 @@
* @ngdoc method
* @name $httpBackend#expectPOST
* @description
* Creates a new request expectation for POST requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
@@ -1719,11 +1727,11 @@
* @ngdoc method
* @name $httpBackend#expectPUT
* @description
* Creates a new request expectation for PUT requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
@@ -1737,11 +1745,11 @@
* @ngdoc method
* @name $httpBackend#expectPATCH
* @description
* Creates a new request expectation for PATCH requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
@@ -1755,11 +1763,11 @@
* @ngdoc method
* @name $httpBackend#expectJSONP
* @description
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives an url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives an url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described above.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* request is handled. You can save this object for later use and invoke `respond` again in
* order to change how a matched request is handled.
@@ -1786,28 +1794,38 @@
/**
* @ngdoc method
* @name $httpBackend#flush
* @description
- * Flushes all pending requests using the trained responses.
+ * Flushes pending requests using the trained responses. Requests are flushed in the order they
+ * were made, but it is also possible to skip one or more requests (for example to have them
+ * flushed later). This is useful for simulating scenarios where responses arrive from the server
+ * in any order.
*
- * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
- * all pending requests will be flushed. If there are no pending requests when the flush method
- * is called an exception is thrown (as this typically a sign of programming error).
+ * If there are no pending requests to flush when the method is called, an exception is thrown (as
+ * this is typically a sign of programming error).
+ *
+ * @param {number=} count - Number of responses to flush. If undefined/null, all pending requests
+ * (starting after `skip`) will be flushed.
+ * @param {number=} [skip=0] - Number of pending requests to skip. For example, a value of `5`
+ * would skip the first 5 pending requests and start flushing from the 6th onwards.
*/
- $httpBackend.flush = function(count, digest) {
+ $httpBackend.flush = function(count, skip, digest) {
if (digest !== false) $rootScope.$digest();
- if (!responses.length) throw new Error('No pending request to flush !');
+ skip = skip || 0;
+ if (skip >= responses.length) throw new Error('No pending request to flush !');
+
if (angular.isDefined(count) && count !== null) {
while (count--) {
- if (!responses.length) throw new Error('No more pending request to flush !');
- responses.shift()();
+ var part = responses.splice(skip, 1);
+ if (!part.length) throw new Error('No more pending request to flush !');
+ part[0]();
}
} else {
- while (responses.length) {
- responses.shift()();
+ while (responses.length > skip) {
+ responses.splice(skip, 1)[0]();
}
}
$httpBackend.verifyNoOutstandingExpectation(digest);
};
@@ -1845,11 +1863,12 @@
*
* ```js
* afterEach($httpBackend.verifyNoOutstandingRequest);
* ```
*/
- $httpBackend.verifyNoOutstandingRequest = function() {
+ $httpBackend.verifyNoOutstandingRequest = function(digest) {
+ if (digest !== false) $rootScope.$digest();
if (responses.length) {
throw new Error('Unflushed requests: ' + responses.length);
}
};
@@ -1871,49 +1890,67 @@
function createShortMethods(prefix) {
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
$httpBackend[prefix + method] = function(url, headers, keys) {
+ assertArgDefined(arguments, 0, 'url');
+
+ // Change url to `null` if `undefined` to stop it throwing an exception further down
+ if (angular.isUndefined(url)) url = null;
+
return $httpBackend[prefix](method, url, undefined, headers, keys);
};
});
angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
$httpBackend[prefix + method] = function(url, data, headers, keys) {
+ assertArgDefined(arguments, 0, 'url');
+
+ // Change url to `null` if `undefined` to stop it throwing an exception further down
+ if (angular.isUndefined(url)) url = null;
+
return $httpBackend[prefix](method, url, data, headers, keys);
};
});
}
}
+function assertArgDefined(args, index, name) {
+ if (args.length > index && angular.isUndefined(args[index])) {
+ throw new Error('Undefined argument `' + name + '`; the argument is provided but not defined');
+ }
+}
+
+
function MockHttpExpectation(method, url, data, headers, keys) {
function getUrlParams(u) {
var params = u.slice(u.indexOf('?') + 1).split('&');
return params.sort();
}
function compareUrl(u) {
- return (url.slice(0, url.indexOf('?')) == u.slice(0, u.indexOf('?')) && getUrlParams(url).join() == getUrlParams(u).join());
+ return (url.slice(0, url.indexOf('?')) === u.slice(0, u.indexOf('?')) &&
+ getUrlParams(url).join() === getUrlParams(u).join());
}
this.data = data;
this.headers = headers;
this.match = function(m, u, d, h) {
- if (method != m) return false;
+ if (method !== m) return false;
if (!this.matchUrl(u)) return false;
if (angular.isDefined(d) && !this.matchData(d)) return false;
if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
return true;
};
this.matchUrl = function(u) {
if (!url) return true;
if (angular.isFunction(url.test)) return url.test(u);
if (angular.isFunction(url)) return url(u);
- return (url == u || compareUrl(u));
+ return (url === u || compareUrl(u));
};
this.matchHeaders = function(h) {
if (angular.isUndefined(headers)) return true;
if (angular.isFunction(headers)) return headers(h);
@@ -1925,10 +1962,11 @@
if (data && angular.isFunction(data.test)) return data.test(d);
if (data && angular.isFunction(data)) return data(d);
if (data && !angular.isString(data)) {
return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
}
+ // eslint-disable-next-line eqeqeq
return data == d;
};
this.toString = function() {
return method + ' ' + url;
@@ -1956,11 +1994,11 @@
function parseQuery() {
var obj = {}, key_value, key,
queryStr = u.indexOf('?') > -1
? u.substring(u.indexOf('?') + 1)
- : "";
+ : '';
angular.forEach(queryStr.split('&'), function(keyValue) {
if (keyValue) {
key_value = keyValue.replace(/\+/g,'%20').split('=');
key = tryDecodeURIComponent(key_value[0]);
@@ -2023,11 +2061,11 @@
header = this.$$respHeaders[name];
if (header) return header;
header = undefined;
angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
- if (!header && angular.lowercase(headerName) == name) header = headerVal;
+ if (!header && angular.lowercase(headerName) === name) header = headerVal;
});
return header;
};
this.getAllResponseHeaders = function() {
@@ -2096,11 +2134,11 @@
};
function formatPendingTasksAsString(tasks) {
var result = [];
angular.forEach(tasks, function(task) {
- result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
+ result.push('{id: ' + task.id + ', time: ' + task.time + '}');
});
return result.join(', ');
}
@@ -2151,11 +2189,15 @@
* @name $controller
* @description
* A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
* controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
*
+ * Depending on the value of
+ * {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
+ * will be bound before or after invoking the constructor.
*
+ *
* ## Example
*
* ```js
*
* // Directive definition ...
@@ -2169,22 +2211,28 @@
*
*
* // Controller definition ...
*
* myMod.controller('MyDirectiveController', ['$log', function($log) {
- * $log.info(this.name);
+ * this.log = function() {
+ * $log.info(this.name);
+ * };
* }]);
*
*
* // In a test ...
*
* describe('myDirectiveController', function() {
- * it('should write the bound name to the log', inject(function($controller, $log) {
- * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
- * expect(ctrl.name).toEqual('Clark Kent');
- * expect($log.info.logs).toEqual(['Clark Kent']);
- * }));
+ * describe('log()', function() {
+ * it('should write the bound name to the log', inject(function($controller, $log) {
+ * var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
+ * ctrl.log();
+ *
+ * expect(ctrl.name).toEqual('Clark Kent');
+ * expect($log.info.logs).toEqual(['Clark Kent']);
+ * }));
+ * });
* });
*
* ```
*
* @param {Function|string} constructor If called with a function then it's considered to be the
@@ -2192,56 +2240,74 @@
* to retrieve the controller constructor using the following steps:
*
* * check if a controller with given name is registered via `$controllerProvider`
* * check if evaluating the string on the current scope returns a constructor
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
- * `window` object (not recommended)
+ * `window` object (deprecated, not recommended)
*
* The string can use the `controller as property` syntax, where the controller instance is published
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
* to work correctly.
*
* @param {Object} locals Injection locals for Controller.
- * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
- * to simulate the `bindToController` feature and simplify certain kinds of tests.
+ * @param {Object=} bindings Properties to add to the controller instance. This is used to simulate
+ * the `bindToController` feature and simplify certain kinds of tests.
* @return {Object} Instance of given controller.
*/
-angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
- return function(expression, locals, later, ident) {
- if (later && typeof later === 'object') {
- var instantiate = $delegate(expression, locals, true, ident);
- angular.extend(instantiate.instance, later);
+function createControllerDecorator(compileProvider) {
+ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
+ return function(expression, locals, later, ident) {
+ if (later && typeof later === 'object') {
+ var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
- var instance = instantiate();
- if (instance !== instantiate.instance) {
- angular.extend(instance, later);
+ var instantiate = $delegate(expression, locals, true, ident);
+ if (preAssignBindingsEnabled) {
+ angular.extend(instantiate.instance, later);
+ }
+
+ var instance = instantiate();
+ if (!preAssignBindingsEnabled || instance !== instantiate.instance) {
+ angular.extend(instance, later);
+ }
+
+ return instance;
}
+ return $delegate(expression, locals, later, ident);
+ };
+ }];
- return instance;
- }
- return $delegate(expression, locals, later, ident);
- };
-}];
+ return angular.mock.$ControllerDecorator;
+}
/**
* @ngdoc service
* @name $componentController
* @description
- * A service that can be used to create instances of component controllers.
- * <div class="alert alert-info">
+ * A service that can be used to create instances of component controllers. Useful for unit-testing.
+ *
* Be aware that the controller will be instantiated and attached to the scope as specified in
* the component definition object. If you do not provide a `$scope` object in the `locals` param
* then the helper will create a new isolated scope as a child of `$rootScope`.
- * </div>
+ *
+ * If you are using `$element` or `$attrs` in the controller, make sure to provide them as `locals`.
+ * The `$element` must be a jqLite-wrapped DOM element, and `$attrs` should be an object that
+ * has all properties / functions that you are using in the controller. If this is getting too complex,
+ * you should compile the component instead and access the component's controller via the
+ * {@link angular.element#methods `controller`} function.
+ *
+ * See also the section on {@link guide/component#unit-testing-component-controllers unit-testing component controllers}
+ * in the guide.
+ *
* @param {string} componentName the name of the component whose controller we want to instantiate
* @param {Object} locals Injection locals for Controller.
* @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
* to simulate the `bindToController` feature and simplify certain kinds of tests.
* @param {string=} ident Override the property name to use when attaching the controller to the scope.
* @return {Object} Instance of requested controller.
*/
-angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
+angular.mock.$ComponentControllerProvider = ['$compileProvider',
+ function ComponentControllerProvider($compileProvider) {
this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
return function $componentController(componentName, locals, bindings, ident) {
// get all directives associated to the component name
var directives = $injector.get(componentName + 'Directive');
// look for those directives that are components
@@ -2286,10 +2352,11 @@
*
* First, download the file:
* * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
* `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
* * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
+ * * [Yarn](https://yarnpkg.com) e.g. `yarn add angular-mocks@X.Y.Z`
* * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
* * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
* `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
*
* where X.Y.Z is the AngularJS version you are running.
@@ -2317,15 +2384,15 @@
$log: angular.mock.$LogProvider,
$interval: angular.mock.$IntervalProvider,
$httpBackend: angular.mock.$HttpBackendProvider,
$rootElement: angular.mock.$RootElementProvider,
$componentController: angular.mock.$ComponentControllerProvider
-}).config(['$provide', function($provide) {
+}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
- $provide.decorator('$controller', angular.mock.$ControllerDecorator);
+ $provide.decorator('$controller', createControllerDecorator($compileProvider));
}]);
/**
* @ngdoc module
* @name ngMockE2E
@@ -2385,11 +2452,11 @@
* $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
* var phone = angular.fromJson(data);
* phones.push(phone);
* return [200, phone, {}];
* });
- * $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templare are handled by the real server
+ * $httpBackend.whenGET(/^\/templates\//).passThrough(); // Requests for templates are handled by the real server
* //...
* });
* ```
*
* Afterwards, bootstrap your app with this new module.
@@ -2397,11 +2464,11 @@
* ## Example
* <example name="httpbackend-e2e-testing" module="myAppE2E" deps="angular-mocks.js">
* <file name="app.js">
* var myApp = angular.module('myApp', []);
*
- * myApp.controller('main', function($http) {
+ * myApp.controller('MainCtrl', function MainCtrl($http) {
* var ctrl = this;
*
* ctrl.phones = [];
* ctrl.newPhone = {
* name: ''
@@ -2439,11 +2506,11 @@
* return [200, phone, {}];
* });
* });
* </file>
* <file name="index.html">
- * <div ng-controller="main as $ctrl">
+ * <div ng-controller="MainCtrl as $ctrl">
* <form name="newPhoneForm" ng-submit="$ctrl.addPhone($ctrl.newPhone)">
* <input type="text" ng-model="$ctrl.newPhone.name">
* <input type="submit" value="Add Phone">
* </form>
* <h1>Phones</h1>
@@ -2463,11 +2530,11 @@
* @module ngMockE2E
* @description
* Creates a new backend definition.
*
* @param {string} method HTTP method.
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
@@ -2495,11 +2562,11 @@
* @name $httpBackend#whenGET
* @module ngMockE2E
* @description
* Creates a new backend definition for GET requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
* @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2512,11 +2579,11 @@
* @name $httpBackend#whenHEAD
* @module ngMockE2E
* @description
* Creates a new backend definition for HEAD requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
* @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2529,11 +2596,11 @@
* @name $httpBackend#whenDELETE
* @module ngMockE2E
* @description
* Creates a new backend definition for DELETE requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
* @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
@@ -2546,11 +2613,11 @@
* @name $httpBackend#whenPOST
* @module ngMockE2E
* @description
* Creates a new backend definition for POST requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
@@ -2564,11 +2631,11 @@
* @name $httpBackend#whenPUT
* @module ngMockE2E
* @description
* Creates a new backend definition for PUT requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
@@ -2582,11 +2649,11 @@
* @name $httpBackend#whenPATCH
* @module ngMockE2E
* @description
* Creates a new backend definition for PATCH requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(string|RegExp)=} data HTTP request body.
* @param {(Object|function(Object))=} headers HTTP headers.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
@@ -2600,11 +2667,11 @@
* @name $httpBackend#whenJSONP
* @module ngMockE2E
* @description
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
- * @param {string|RegExp|function(string)} url HTTP url or function that receives a url
+ * @param {string|RegExp|function(string)=} url HTTP url or function that receives a url
* and returns true if the url matches the current definition.
* @param {(Array)=} keys Array of keys to assign to regex matches in request url described on
* {@link ngMock.$httpBackend $httpBackend mock}.
* @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
* control how a matched request is handled. You can save this object for later use and invoke
@@ -2652,19 +2719,19 @@
/**
* @ngdoc method
* @name $rootScope.Scope#$countChildScopes
* @module ngMock
+ * @this $rootScope.Scope
* @description
* Counts all the direct and indirect child scopes of the current scope.
*
* The current scope is excluded from the count. The count includes all isolate child scopes.
*
* @returns {number} Total number of child scopes.
*/
function countChildScopes() {
- // jshint validthis: true
var count = 0; // exclude the current scope
var pendingChildHeads = [this.$$childHead];
var currentScope;
while (pendingChildHeads.length) {
@@ -2682,21 +2749,21 @@
/**
* @ngdoc method
* @name $rootScope.Scope#$countWatchers
+ * @this $rootScope.Scope
* @module ngMock
* @description
* Counts all the watchers of direct and indirect child scopes of the current scope.
*
* The watchers of the current scope are included in the count and so are all the watchers of
* isolate child scopes.
*
* @returns {number} Total number of watchers.
*/
function countWatchers() {
- // jshint validthis: true
var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope
var pendingChildHeads = [this.$$childHead];
var currentScope;
while (pendingChildHeads.length) {
@@ -2712,11 +2779,11 @@
return count;
}
}];
-!(function(jasmineOrMocha) {
+(function(jasmineOrMocha) {
if (!jasmineOrMocha) {
return;
}
@@ -2807,11 +2874,11 @@
* `before()` methods. Call `module.sharedInjector()` before you setup any other hooks that
* will create (i.e call `module()`) or use (i.e call `inject()`) the injector.
*
* You cannot call `sharedInjector()` from within a context already using `sharedInjector()`.
*
- * ## Example
+ * ## Example
*
* Typically beforeAll is used to make many assertions about a single operation. This can
* cut down test run-time as the test setup doesn't need to be re-run, and enabling focussed
* tests each with a single assertion.
*
@@ -2845,18 +2912,18 @@
*
* ```
*/
module.sharedInjector = function() {
if (!(module.$$beforeAllHook && module.$$afterAllHook)) {
- throw Error("sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll");
+ throw Error('sharedInjector() cannot be used unless your test runner defines beforeAll/afterAll');
}
var initialized = false;
- module.$$beforeAllHook(function() {
+ module.$$beforeAllHook(/** @this */ function() {
if (injectorState.shared) {
- injectorState.sharedError = Error("sharedInjector() cannot be called inside a context that has already called sharedInjector()");
+ injectorState.sharedError = Error('sharedInjector() cannot be called inside a context that has already called sharedInjector()');
throw injectorState.sharedError;
}
initialized = true;
currentSpec = this;
injectorState.shared = true;
@@ -2871,14 +2938,14 @@
}
});
};
module.$$beforeEach = function() {
- if (injectorState.shared && currentSpec && currentSpec != this) {
+ if (injectorState.shared && currentSpec && currentSpec !== this) {
var state = currentSpec;
currentSpec = this;
- angular.forEach(["$injector","$modules","$providerInjector", "$injectorStrict"], function(k) {
+ angular.forEach(['$injector','$modules','$providerInjector', '$injectorStrict'], function(k) {
currentSpec[k] = state[k];
state[k] = null;
});
} else {
currentSpec = this;
@@ -2965,11 +3032,11 @@
*
* To help with this, the injected parameters can, optionally, be enclosed with underscores.
* These are ignored by the injector when the reference name is resolved.
*
* For example, the parameter `_myService_` would be resolved as the reference `myService`.
- * Since it is available in the function body as _myService_, we can then assign it to a variable
+ * Since it is available in the function body as `_myService_`, we can then assign it to a variable
* defined in an outer scope.
*
* ```
* // Defined out reference variable outside
* var myService;
@@ -3029,11 +3096,11 @@
* @param {...Function} fns any number of functions which will be injected using the injector.
*/
- var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
+ var ErrorAddingDeclarationLocationStack = function ErrorAddingDeclarationLocationStack(e, errorForStack) {
this.message = e.message;
this.name = e.name;
if (e.line) this.line = e.line;
if (e.sourceId) this.sourceId = e.sourceId;
if (e.stack && errorForStack)
@@ -3047,15 +3114,15 @@
var errorForStack = new Error('Declaration Location');
// IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
if (!errorForStack.stack) {
try {
throw errorForStack;
- } catch (e) {}
+ } catch (e) { /* empty */ }
}
- return wasInjectorCreated() ? workFn.call(currentSpec) : workFn;
+ return wasInjectorCreated() ? WorkFn.call(currentSpec) : WorkFn;
/////////////////////
- function workFn() {
+ function WorkFn() {
var modules = currentSpec.$modules || [];
var strictDi = !!currentSpec.$injectorStrict;
modules.unshift(['$injector', function($injector) {
currentSpec.$providerInjector = $injector;
}]);
@@ -3064,11 +3131,11 @@
var injector = currentSpec.$injector;
if (!injector) {
if (strictDi) {
// If strictDi is enabled, annotate the providerInjector blocks
angular.forEach(modules, function(moduleFn) {
- if (typeof moduleFn === "function") {
+ if (typeof moduleFn === 'function') {
angular.injector.$$annotate(moduleFn);
}
});
}
injector = currentSpec.$injector = angular.injector(modules, strictDi);
@@ -3079,13 +3146,11 @@
// If the injector is strict / strictDi, and the spec wants to inject using automatic
// annotation, then annotate the function here.
injector.annotate(blockFns[i]);
}
try {
- /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
injector.invoke(blockFns[i] || angular.noop, this);
- /* jshint +W040 */
} catch (e) {
if (e.stack && errorForStack) {
throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
}
throw e;
@@ -3119,8 +3184,221 @@
this.cleanupAfterEach = function() {
return !this.shared || this.sharedError;
};
}
})(window.jasmine || window.mocha);
+
+'use strict';
+
+(function() {
+ /**
+ * Triggers a browser event. Attempts to choose the right event if one is
+ * not specified.
+ *
+ * @param {Object} element Either a wrapped jQuery/jqLite node or a DOMElement
+ * @param {string} eventType Optional event type
+ * @param {Object=} eventData An optional object which contains additional event data (such as x,y
+ * coordinates, keys, etc...) that are passed into the event when triggered
+ */
+ window.browserTrigger = function browserTrigger(element, eventType, eventData) {
+ if (element && !element.nodeName) element = element[0];
+ if (!element) return;
+
+ eventData = eventData || {};
+ var relatedTarget = eventData.relatedTarget || element;
+ var keys = eventData.keys;
+ var x = eventData.x;
+ var y = eventData.y;
+
+ var inputType = (element.type) ? element.type.toLowerCase() : null,
+ nodeName = element.nodeName.toLowerCase();
+ if (!eventType) {
+ eventType = {
+ 'text': 'change',
+ 'textarea': 'change',
+ 'hidden': 'change',
+ 'password': 'change',
+ 'button': 'click',
+ 'submit': 'click',
+ 'reset': 'click',
+ 'image': 'click',
+ 'checkbox': 'click',
+ 'radio': 'click',
+ 'select-one': 'change',
+ 'select-multiple': 'change',
+ '_default_': 'click'
+ }[inputType || '_default_'];
+ }
+
+ if (nodeName === 'option') {
+ element.parentNode.value = element.value;
+ element = element.parentNode;
+ eventType = 'change';
+ }
+
+ keys = keys || [];
+ function pressed(key) {
+ return keys.indexOf(key) !== -1;
+ }
+
+ var evnt;
+ if (/transitionend/.test(eventType)) {
+ if (window.WebKitTransitionEvent) {
+ evnt = new window.WebKitTransitionEvent(eventType, eventData);
+ evnt.initEvent(eventType, false, true);
+ } else {
+ try {
+ evnt = new window.TransitionEvent(eventType, eventData);
+ } catch (e) {
+ evnt = window.document.createEvent('TransitionEvent');
+ evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
+ }
+ }
+ } else if (/animationend/.test(eventType)) {
+ if (window.WebKitAnimationEvent) {
+ evnt = new window.WebKitAnimationEvent(eventType, eventData);
+ evnt.initEvent(eventType, false, true);
+ } else {
+ try {
+ evnt = new window.AnimationEvent(eventType, eventData);
+ } catch (e) {
+ evnt = window.document.createEvent('AnimationEvent');
+ evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
+ }
+ }
+ } else if (/touch/.test(eventType) && supportsTouchEvents()) {
+ evnt = createTouchEvent(element, eventType, x, y);
+ } else if (/key/.test(eventType)) {
+ evnt = window.document.createEvent('Events');
+ evnt.initEvent(eventType, eventData.bubbles, eventData.cancelable);
+ evnt.view = window;
+ evnt.ctrlKey = pressed('ctrl');
+ evnt.altKey = pressed('alt');
+ evnt.shiftKey = pressed('shift');
+ evnt.metaKey = pressed('meta');
+ evnt.keyCode = eventData.keyCode;
+ evnt.charCode = eventData.charCode;
+ evnt.which = eventData.which;
+ } else {
+ evnt = window.document.createEvent('MouseEvents');
+ x = x || 0;
+ y = y || 0;
+ evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
+ pressed('alt'), pressed('shift'), pressed('meta'), 0, relatedTarget);
+ }
+
+ /* we're unable to change the timeStamp value directly so this
+ * is only here to allow for testing where the timeStamp value is
+ * read */
+ evnt.$manualTimeStamp = eventData.timeStamp;
+
+ if (!evnt) return;
+
+ var originalPreventDefault = evnt.preventDefault,
+ appWindow = element.ownerDocument.defaultView,
+ fakeProcessDefault = true,
+ finalProcessDefault,
+ angular = appWindow.angular || {};
+
+ // igor: temporary fix for https://bugzilla.mozilla.org/show_bug.cgi?id=684208
+ angular['ff-684208-preventDefault'] = false;
+ evnt.preventDefault = function() {
+ fakeProcessDefault = false;
+ return originalPreventDefault.apply(evnt, arguments);
+ };
+
+ if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
+ element.dispatchEvent(evnt);
+ } else {
+ triggerForPath(element, evnt);
+ }
+
+ finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
+
+ delete angular['ff-684208-preventDefault'];
+
+ return finalProcessDefault;
+ };
+
+ function supportsTouchEvents() {
+ if ('_cached' in supportsTouchEvents) {
+ return supportsTouchEvents._cached;
+ }
+ if (!window.document.createTouch || !window.document.createTouchList) {
+ supportsTouchEvents._cached = false;
+ return false;
+ }
+ try {
+ window.document.createEvent('TouchEvent');
+ } catch (e) {
+ supportsTouchEvents._cached = false;
+ return false;
+ }
+ supportsTouchEvents._cached = true;
+ return true;
+ }
+
+ function createTouchEvent(element, eventType, x, y) {
+ var evnt = new window.Event(eventType);
+ x = x || 0;
+ y = y || 0;
+
+ var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
+ var touches = window.document.createTouchList(touch);
+
+ evnt.touches = touches;
+
+ return evnt;
+ }
+
+ function supportsEventBubblingInDetachedTree() {
+ if ('_cached' in supportsEventBubblingInDetachedTree) {
+ return supportsEventBubblingInDetachedTree._cached;
+ }
+ supportsEventBubblingInDetachedTree._cached = false;
+ var doc = window.document;
+ if (doc) {
+ var parent = doc.createElement('div'),
+ child = parent.cloneNode();
+ parent.appendChild(child);
+ parent.addEventListener('e', function() {
+ supportsEventBubblingInDetachedTree._cached = true;
+ });
+ var evnt = window.document.createEvent('Events');
+ evnt.initEvent('e', true, true);
+ child.dispatchEvent(evnt);
+ }
+ return supportsEventBubblingInDetachedTree._cached;
+ }
+
+ function triggerForPath(element, evnt) {
+ var stop = false;
+
+ var _stopPropagation = evnt.stopPropagation;
+ evnt.stopPropagation = function() {
+ stop = true;
+ _stopPropagation.apply(evnt, arguments);
+ };
+ patchEventTargetForBubbling(evnt, element);
+ do {
+ element.dispatchEvent(evnt);
+ // eslint-disable-next-line no-unmodified-loop-condition
+ } while (!stop && (element = element.parentNode));
+ }
+
+ function patchEventTargetForBubbling(event, target) {
+ event._target = target;
+ Object.defineProperty(event, 'target', {get: function() { return this._target;}});
+ }
+
+ function isAttachedToDocument(element) {
+ while ((element = element.parentNode)) {
+ if (element === window) {
+ return true;
+ }
+ }
+ return false;
+ }
+})();
})(window, window.angular);