Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8302981a08 | |||
| d2a7b5162f | |||
| 2ecd85b989 | |||
| 2bdf712687 | |||
| 0de02973c1 | |||
| 55516da2df | |||
| e8adbf8d5a | |||
| c169c60535 | |||
| 0b6ec6b3ab | |||
| d2511cfac0 | |||
| 155efa421b | |||
| f33d95cfcf | |||
| 57b626a673 |
+4
-4
@@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '6'
|
||||
- '8'
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
@@ -28,7 +28,7 @@ env:
|
||||
- secure: oTBjhnOKhs0qDSKTf7fE4f6DYiNDPycvB7qfSF5QRIbJK/LK/J4UtFwetXuXj79HhUZG9qnoT+5e7lPaiaMlpsIKn9ann7ffqFWN1E8TMtpJF+AGigx3djYElwfgf5nEnFUFhwjFzvbfpZNnxVGgX5YbIZpe/WUbHkP4ffU0Wks=
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.27.5
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.3.2
|
||||
- export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
before_script:
|
||||
@@ -54,10 +54,10 @@ notifications:
|
||||
jobs:
|
||||
include:
|
||||
- stage: deploy
|
||||
# Don't deploy from PRs and only from our default branches.
|
||||
# Don't deploy from PRs. Only deploy from our default branches, or if commit is tagged.
|
||||
# This is a Travis-specific boolean language: https://docs.travis-ci.com/user/conditional-builds-stages-jobs#Specifying-conditions
|
||||
# The deployment logic for pushed branches is further defined in scripts\travis\build.sh
|
||||
if: type != pull_request and branch =~ ^(v1\.\d+\.x|master)$
|
||||
if: type != pull_request and (branch =~ ^(v1\.\d+\.x|master)$ or tag IS present)
|
||||
env:
|
||||
- JOB=deploy
|
||||
before_script: skip
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
<a name="1.6.8"></a>
|
||||
# 1.6.8 beneficial-tincture (2017-12-18)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
- **$location:**
|
||||
- always decode special chars in `$location.url(value)`
|
||||
([2bdf71](https://github.com/angular/angular.js/commit/2bdf7126878c87474bb7588ce093d0a3c57b0026))
|
||||
- decode non-component special chars in Hashbang URLS
|
||||
([57b626](https://github.com/angular/angular.js/commit/57b626a673b7530399d3377dfe770165bec35f8a))
|
||||
- **ngModelController:** allow $overrideModelOptions to set updateOn
|
||||
([55516d](https://github.com/angular/angular.js/commit/55516da2dfc7c5798dce24e9fa930c5ac90c900c),
|
||||
[#16351](https://github.com/angular/angular.js/issues/16351),
|
||||
[#16364](https://github.com/angular/angular.js/issues/16364))
|
||||
|
||||
|
||||
## New Features
|
||||
- **$parse:** add a hidden interface to retrieve an expression's AST
|
||||
([f33d95](https://github.com/angular/angular.js/commit/f33d95cfcff6fd0270f92a142df8794cca2013ad),
|
||||
[#16253](https://github.com/angular/angular.js/issues/16253),
|
||||
[#16260](https://github.com/angular/angular.js/issues/16260))
|
||||
|
||||
<a name="1.6.7"></a>
|
||||
# 1.6.7 imperial-backstroke (2017-11-24)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="alert alert-danger">
|
||||
**Note:** In the past, end-to-end testing could be done with a deprecated tool called
|
||||
[Angular Scenario Runner](http://code.angularjs.org/1.2.16/docs/guide/e2e-testing). That tool
|
||||
is now in maintenance mode.
|
||||
is now in maintenance mode, and will be removed in version 1.7.0.
|
||||
</div>
|
||||
|
||||
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6.9.1",
|
||||
"yarn": ">=0.21.3",
|
||||
"node": "^8.9.1",
|
||||
"yarn": ">=1.3.2",
|
||||
"grunt": "^1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -8,7 +8,7 @@ nvm install
|
||||
|
||||
# clean out and install yarn
|
||||
rm -rf ~/.yarn
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.21.3
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.3.2
|
||||
export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
# Ensure that we have the local dependencies installed
|
||||
|
||||
@@ -67,7 +67,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
@@ -270,6 +270,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
|
||||
this.$name = $interpolate($attr.name || '', false)($scope);
|
||||
this.$$parentForm = nullFormCtrl;
|
||||
this.$options = defaultModelOptions;
|
||||
this.$$updateEvents = '';
|
||||
// Attach the correct context to the event handler function for updateOn
|
||||
this.$$updateEventHandler = this.$$updateEventHandler.bind(this);
|
||||
|
||||
this.$$parsedNgModel = $parse($attr.ngModel);
|
||||
this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
|
||||
@@ -875,11 +878,22 @@ NgModelController.prototype = {
|
||||
* See {@link ngModelOptions} for information about what options can be specified
|
||||
* and how model option inheritance works.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** this function only affects the options set on the `ngModelController`,
|
||||
* and not the options on the {@link ngModelOptions} directive from which they might have been
|
||||
* obtained initially.
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* **Note:** it is not possible to override the `getterSetter` option.
|
||||
* </div>
|
||||
*
|
||||
* @param {Object} options a hash of settings to override the previous options
|
||||
*
|
||||
*/
|
||||
$overrideModelOptions: function(options) {
|
||||
this.$options = this.$options.createChild(options);
|
||||
this.$$setUpdateOnEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1027,6 +1041,21 @@ NgModelController.prototype = {
|
||||
this.$modelValue = this.$$rawModelValue = modelValue;
|
||||
this.$$parserValid = undefined;
|
||||
this.$processModelValue();
|
||||
},
|
||||
|
||||
$$setUpdateOnEvents: function() {
|
||||
if (this.$$updateEvents) {
|
||||
this.$$element.off(this.$$updateEvents, this.$$updateEventHandler);
|
||||
}
|
||||
|
||||
this.$$updateEvents = this.$options.getOption('updateOn');
|
||||
if (this.$$updateEvents) {
|
||||
this.$$element.on(this.$$updateEvents, this.$$updateEventHandler);
|
||||
}
|
||||
},
|
||||
|
||||
$$updateEventHandler: function(ev) {
|
||||
this.$$debounceViewValueCommit(ev && ev.type);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1318,11 +1347,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
|
||||
},
|
||||
post: function ngModelPostLink(scope, element, attr, ctrls) {
|
||||
var modelCtrl = ctrls[0];
|
||||
if (modelCtrl.$options.getOption('updateOn')) {
|
||||
element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
|
||||
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
|
||||
});
|
||||
}
|
||||
modelCtrl.$$setUpdateOnEvents();
|
||||
|
||||
function setTouched() {
|
||||
modelCtrl.$setTouched();
|
||||
|
||||
@@ -102,7 +102,7 @@ defaultModelOptions = new ModelOptions({
|
||||
*
|
||||
* The `ngModelOptions` settings are found by evaluating the value of the attribute directive as
|
||||
* an Angular expression. This expression should evaluate to an object, whose properties contain
|
||||
* the settings. For example: `<div "ng-model-options"="{ debounce: 100 }"`.
|
||||
* the settings. For example: `<div ng-model-options="{ debounce: 100 }"`.
|
||||
*
|
||||
* ## Inheriting Options
|
||||
*
|
||||
@@ -177,6 +177,8 @@ defaultModelOptions = new ModelOptions({
|
||||
* `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
|
||||
* to have access to the updated model.
|
||||
*
|
||||
* ### Overriding immediate updates
|
||||
*
|
||||
* The following example shows how to override immediate updates. Changes on the inputs within the
|
||||
* form will update the model only when the control loses focus (blur event). If `escape` key is
|
||||
* pressed while the input field is focused, the value is reset to the value in the current model.
|
||||
@@ -236,6 +238,8 @@ defaultModelOptions = new ModelOptions({
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* ### Debouncing updates
|
||||
*
|
||||
* The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
|
||||
* If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
|
||||
*
|
||||
@@ -260,6 +264,7 @@ defaultModelOptions = new ModelOptions({
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
*
|
||||
* ## Model updates and validation
|
||||
*
|
||||
* The default behaviour in `ngModel` is that the model value is set to `undefined` when the
|
||||
@@ -307,20 +312,41 @@ defaultModelOptions = new ModelOptions({
|
||||
* You can specify the timezone that date/time input directives expect by providing its name in the
|
||||
* `timezone` property.
|
||||
*
|
||||
*
|
||||
* ## Programmatically changing options
|
||||
*
|
||||
* The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
|
||||
* watched for changes. However, it is possible to override the options on a single
|
||||
* {@link ngModel.NgModelController} instance with
|
||||
* {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
|
||||
*
|
||||
*
|
||||
* @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
|
||||
* and its descendents. Valid keys are:
|
||||
* - `updateOn`: string specifying which event should the input be bound to. You can set several
|
||||
* events using an space delimited list. There is a special event called `default` that
|
||||
* matches the default events belonging to the control.
|
||||
* matches the default events belonging to the control. These are the events that are bound to
|
||||
* the control, and when fired, update the `$viewValue` via `$setViewValue`.
|
||||
*
|
||||
* `ngModelOptions` considers every event that is not listed in `updateOn` a "default" event,
|
||||
* since different control types use different default events.
|
||||
*
|
||||
* See also the section {@link ngModelOptions#triggering-and-debouncing-model-updates
|
||||
* Triggering and debouncing model updates}.
|
||||
*
|
||||
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
|
||||
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
|
||||
* custom value for each event. For example:
|
||||
* ```
|
||||
* ng-model-options="{
|
||||
* updateOn: 'default blur',
|
||||
* updateOn: 'default blur click',
|
||||
* debounce: { 'default': 500, 'blur': 0 }
|
||||
* }"
|
||||
* ```
|
||||
*
|
||||
* "default" also applies to all events that are listed in `updateOn` but are not
|
||||
* listed in `debounce`, i.e. "click" would also be debounced by 500 milliseconds.
|
||||
*
|
||||
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
|
||||
* not validate correctly instead of the default behavior of setting the model to undefined.
|
||||
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
* @element ANY
|
||||
*
|
||||
* @description
|
||||
* The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
|
||||
* DOM element. This is useful if the element contains what appears to be Angular directives and
|
||||
* bindings but which should be ignored by Angular. This could be the case if you have a site that
|
||||
* displays snippets of code, for instance.
|
||||
* The `ngNonBindable` directive tells AngularJS not to compile or bind the contents of the current
|
||||
* DOM element, including directives on the element itself that have a lower priority than
|
||||
* `ngNonBindable`. This is useful if the element contains what appears to be AngularJS directives
|
||||
* and bindings but which should be ignored by AngularJS. This could be the case if you have a site
|
||||
* that displays snippets of code, for instance.
|
||||
*
|
||||
* @example
|
||||
* In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
|
||||
|
||||
@@ -333,9 +333,7 @@ function $InterpolateProvider() {
|
||||
var lastValue;
|
||||
return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
|
||||
var currValue = compute(values);
|
||||
if (isFunction(listener)) {
|
||||
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
|
||||
}
|
||||
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
|
||||
lastValue = currValue;
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -422,7 +422,7 @@ var locationPrototype = {
|
||||
}
|
||||
|
||||
var match = PATH_MATCH.exec(url);
|
||||
if (match[1] || url === '') this.path(decodeURI(match[1]));
|
||||
if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
|
||||
if (match[2] || match[1] || url === '') this.search(match[3] || '');
|
||||
this.hash(match[5] || '');
|
||||
|
||||
|
||||
+28
-10
@@ -1644,11 +1644,26 @@ Parser.prototype = {
|
||||
constructor: Parser,
|
||||
|
||||
parse: function(text) {
|
||||
var ast = this.ast.ast(text);
|
||||
var fn = this.astCompiler.compile(ast);
|
||||
fn.literal = isLiteral(ast);
|
||||
fn.constant = isConstant(ast);
|
||||
var ast = this.getAst(text);
|
||||
var fn = this.astCompiler.compile(ast.ast);
|
||||
fn.literal = isLiteral(ast.ast);
|
||||
fn.constant = isConstant(ast.ast);
|
||||
fn.oneTime = ast.oneTime;
|
||||
return fn;
|
||||
},
|
||||
|
||||
getAst: function(exp) {
|
||||
var oneTime = false;
|
||||
exp = exp.trim();
|
||||
|
||||
if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
|
||||
oneTime = true;
|
||||
exp = exp.substring(2);
|
||||
}
|
||||
return {
|
||||
ast: this.ast.ast(exp),
|
||||
oneTime: oneTime
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1771,10 +1786,11 @@ function $ParseProvider() {
|
||||
isIdentifierStart: isFunction(identStart) && identStart,
|
||||
isIdentifierContinue: isFunction(identContinue) && identContinue
|
||||
};
|
||||
$parse.$$getAst = $$getAst;
|
||||
return $parse;
|
||||
|
||||
function $parse(exp, interceptorFn) {
|
||||
var parsedExpression, oneTime, cacheKey;
|
||||
var parsedExpression, cacheKey;
|
||||
|
||||
switch (typeof exp) {
|
||||
case 'string':
|
||||
@@ -1784,16 +1800,12 @@ function $ParseProvider() {
|
||||
parsedExpression = cache[cacheKey];
|
||||
|
||||
if (!parsedExpression) {
|
||||
if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
|
||||
oneTime = true;
|
||||
exp = exp.substring(2);
|
||||
}
|
||||
var lexer = new Lexer($parseOptions);
|
||||
var parser = new Parser(lexer, $filter, $parseOptions);
|
||||
parsedExpression = parser.parse(exp);
|
||||
if (parsedExpression.constant) {
|
||||
parsedExpression.$$watchDelegate = constantWatchDelegate;
|
||||
} else if (oneTime) {
|
||||
} else if (parsedExpression.oneTime) {
|
||||
parsedExpression.$$watchDelegate = parsedExpression.literal ?
|
||||
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
|
||||
} else if (parsedExpression.inputs) {
|
||||
@@ -1811,6 +1823,12 @@ function $ParseProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
function $$getAst(exp) {
|
||||
var lexer = new Lexer($parseOptions);
|
||||
var parser = new Parser(lexer, $filter, $parseOptions);
|
||||
return parser.getAst(exp).ast;
|
||||
}
|
||||
|
||||
function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
|
||||
|
||||
if (newValue == null || oldValueOfValue == null) { // null/undefined
|
||||
|
||||
+3
-6
@@ -394,14 +394,15 @@ function $RootScopeProvider() {
|
||||
*/
|
||||
$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
|
||||
var get = $parse(watchExp);
|
||||
var fn = isFunction(listener) ? listener : noop;
|
||||
|
||||
if (get.$$watchDelegate) {
|
||||
return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
|
||||
return get.$$watchDelegate(this, fn, objectEquality, get, watchExp);
|
||||
}
|
||||
var scope = this,
|
||||
array = scope.$$watchers,
|
||||
watcher = {
|
||||
fn: listener,
|
||||
fn: fn,
|
||||
last: initWatchVal,
|
||||
get: get,
|
||||
exp: prettyPrintExpression || watchExp,
|
||||
@@ -410,10 +411,6 @@ function $RootScopeProvider() {
|
||||
|
||||
lastDirtyWatch = null;
|
||||
|
||||
if (!isFunction(listener)) {
|
||||
watcher.fn = noop;
|
||||
}
|
||||
|
||||
if (!array) {
|
||||
array = scope.$$watchers = [];
|
||||
array.$$digestWatchIndex = -1;
|
||||
|
||||
@@ -34,7 +34,7 @@ function parseTextLiteral(text) {
|
||||
parsedFn['$$watchDelegate'] = function watchDelegate(scope, listener, objectEquality) {
|
||||
var unwatch = scope['$watch'](noop,
|
||||
function textLiteralWatcher() {
|
||||
if (isFunction(listener)) { listener(text, text, scope); }
|
||||
listener(text, text, scope);
|
||||
unwatch();
|
||||
},
|
||||
objectEquality);
|
||||
@@ -58,7 +58,7 @@ function subtractOffset(expressionFn, offset) {
|
||||
parsedFn['$$watchDelegate'] = function watchDelegate(scope, listener, objectEquality) {
|
||||
unwatch = scope['$watch'](expressionFn,
|
||||
function pluralExpressionWatchListener(newValue, oldValue) {
|
||||
if (isFunction(listener)) { listener(minusOffset(newValue), minusOffset(oldValue), scope); }
|
||||
listener(minusOffset(newValue), minusOffset(oldValue), scope);
|
||||
},
|
||||
objectEquality);
|
||||
return unwatch;
|
||||
|
||||
@@ -122,9 +122,7 @@ function InterpolationPartsWatcher(interpolationParts, scope, listener, objectEq
|
||||
|
||||
InterpolationPartsWatcher.prototype.watchListener = function watchListener(newExpressionValues, oldExpressionValues) {
|
||||
var result = this.interpolationParts.getResult(newExpressionValues);
|
||||
if (isFunction(this.listener)) {
|
||||
this.listener.call(null, result, newExpressionValues === oldExpressionValues ? result : this.previousResult, this.scope);
|
||||
}
|
||||
this.listener.call(null, result, newExpressionValues === oldExpressionValues ? result : this.previousResult, this.scope);
|
||||
this.previousResult = result;
|
||||
};
|
||||
|
||||
|
||||
@@ -66,9 +66,7 @@ MessageSelectorWatchers.prototype.expressionFnListener = function expressionFnLi
|
||||
};
|
||||
|
||||
MessageSelectorWatchers.prototype.messageFnListener = function messageFnListener(newMessage, oldMessage) {
|
||||
if (isFunction(this.listener)) {
|
||||
this.listener.call(null, newMessage, newMessage === oldMessage ? newMessage : this.lastMessage, this.scope);
|
||||
}
|
||||
this.listener.call(null, newMessage, newMessage === oldMessage ? newMessage : this.lastMessage, this.scope);
|
||||
this.lastMessage = newMessage;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,13 +2,56 @@
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* Triggers a browser event. Attempts to choose the right event if one is
|
||||
* not specified.
|
||||
* @ngdoc function
|
||||
* @name browserTrigger
|
||||
* @description
|
||||
*
|
||||
* This is a global (window) function that is only available when the {@link ngMock} module is
|
||||
* included.
|
||||
*
|
||||
* It can be used to trigger a native browser event on an element, which is useful for unit testing.
|
||||
*
|
||||
*
|
||||
* @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
|
||||
* @param {string=} eventType Optional event type. If none is specified, the function tries
|
||||
* to determine the right event type for the element, e.g. `change` for
|
||||
* `input[text]`.
|
||||
* @param {Object=} eventData An optional object which contains additional event data that is used
|
||||
* when creating the event:
|
||||
*
|
||||
* - `bubbles`: [Event.bubbles](https://developer.mozilla.org/docs/Web/API/Event/bubbles).
|
||||
* Not applicable to all events.
|
||||
*
|
||||
* - `cancelable`: [Event.cancelable](https://developer.mozilla.org/docs/Web/API/Event/cancelable).
|
||||
* Not applicable to all events.
|
||||
*
|
||||
* - `charcode`: [charCode](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/charcode)
|
||||
* for keyboard events (keydown, keypress, and keyup).
|
||||
*
|
||||
* - `elapsedTime`: the elapsedTime for
|
||||
* [TransitionEvent](https://developer.mozilla.org/docs/Web/API/TransitionEvent)
|
||||
* and [AnimationEvent](https://developer.mozilla.org/docs/Web/API/AnimationEvent).
|
||||
*
|
||||
* - `keycode`: [keyCode](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/keycode)
|
||||
* for keyboard events (keydown, keypress, and keyup).
|
||||
*
|
||||
* - `keys`: an array of possible modifier keys (ctrl, alt, shift, meta) for
|
||||
* [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent) and
|
||||
* keyboard events (keydown, keypress, and keyup).
|
||||
*
|
||||
* - `relatedTarget`: the
|
||||
* [relatedTarget](https://developer.mozilla.org/docs/Web/API/MouseEvent/relatedTarget)
|
||||
* for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent).
|
||||
*
|
||||
* - `which`: [which](https://developer.mozilla.org/docs/Web/API/KeyboardEvent/which)
|
||||
* for keyboard events (keydown, keypress, and keyup).
|
||||
*
|
||||
* - `x`: x-coordinates for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent)
|
||||
* and [TouchEvent](https://developer.mozilla.org/docs/Web/API/TouchEvent).
|
||||
*
|
||||
* - `y`: y-coordinates for [MouseEvent](https://developer.mozilla.org/docs/Web/API/MouseEvent)
|
||||
* and [TouchEvent](https://developer.mozilla.org/docs/Web/API/TouchEvent).
|
||||
*
|
||||
*/
|
||||
window.browserTrigger = function browserTrigger(element, eventType, eventData) {
|
||||
if (element && !element.nodeName) element = element[0];
|
||||
|
||||
@@ -391,6 +391,43 @@ describe('ngModelOptions', function() {
|
||||
browserTrigger(inputElm[2], 'click');
|
||||
expect($rootScope.color).toBe('blue');
|
||||
});
|
||||
|
||||
it('should re-set the trigger events when overridden with $overrideModelOptions', function() {
|
||||
var inputElm = helper.compileInput(
|
||||
'<input type="text" ng-model="name" name="alias" ' +
|
||||
'ng-model-options="{ updateOn: \'blur click\' }"' +
|
||||
'/>');
|
||||
|
||||
var ctrl = inputElm.controller('ngModel');
|
||||
|
||||
helper.changeInputValueTo('a');
|
||||
expect($rootScope.name).toBeUndefined();
|
||||
browserTrigger(inputElm, 'blur');
|
||||
expect($rootScope.name).toEqual('a');
|
||||
|
||||
helper.changeInputValueTo('b');
|
||||
expect($rootScope.name).toBe('a');
|
||||
browserTrigger(inputElm, 'click');
|
||||
expect($rootScope.name).toEqual('b');
|
||||
|
||||
$rootScope.$apply('name = undefined');
|
||||
expect(inputElm.val()).toBe('');
|
||||
ctrl.$overrideModelOptions({updateOn: 'blur mousedown'});
|
||||
|
||||
helper.changeInputValueTo('a');
|
||||
expect($rootScope.name).toBeUndefined();
|
||||
browserTrigger(inputElm, 'blur');
|
||||
expect($rootScope.name).toEqual('a');
|
||||
|
||||
helper.changeInputValueTo('b');
|
||||
expect($rootScope.name).toBe('a');
|
||||
browserTrigger(inputElm, 'click');
|
||||
expect($rootScope.name).toBe('a');
|
||||
|
||||
browserTrigger(inputElm, 'mousedown');
|
||||
expect($rootScope.name).toEqual('b');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -673,6 +673,20 @@ describe('$location', function() {
|
||||
locationUrl.search({'q': '4/5 6'});
|
||||
expect(locationUrl.absUrl()).toEqual('http://host.com?q=4%2F5%206');
|
||||
});
|
||||
|
||||
it('url() should decode non-component special characters in hashbang mode', function() {
|
||||
var locationUrl = new LocationHashbangUrl('http://host.com', 'http://host.com');
|
||||
locationUrl.$$parse('http://host.com');
|
||||
locationUrl.url('/foo%3Abar');
|
||||
expect(locationUrl.path()).toEqual('/foo:bar');
|
||||
});
|
||||
|
||||
it('url() should decode non-component special characters in html5 mode', function() {
|
||||
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com');
|
||||
locationUrl.$$parse('http://host.com');
|
||||
locationUrl.url('/foo%3Abar');
|
||||
expect(locationUrl.path()).toEqual('/foo:bar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3994,4 +3994,48 @@ describe('parser', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('hidden/unsupported features', function() {
|
||||
describe('$$getAst()', function() {
|
||||
it('should be a method exposed on the `$parse` service', inject(function($parse) {
|
||||
expect(isFunction($parse.$$getAst)).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should accept a string expression argument and return the corresponding AST', inject(function($parse) {
|
||||
var ast = $parse.$$getAst('foo.bar');
|
||||
expect(ast).toEqual({
|
||||
type: 'Program',
|
||||
body: [
|
||||
{
|
||||
type: 'ExpressionStatement',
|
||||
expression: {
|
||||
type: 'MemberExpression',
|
||||
object: { type: 'Identifier', name: 'foo' },
|
||||
property: { type: 'Identifier', name: 'bar' },
|
||||
computed: false
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
||||
it('should parse one time binding expressions', inject(function($parse) {
|
||||
var ast = $parse.$$getAst('::foo.bar');
|
||||
expect(ast).toEqual({
|
||||
type: 'Program',
|
||||
body: [
|
||||
{
|
||||
type: 'ExpressionStatement',
|
||||
expression: {
|
||||
type: 'MemberExpression',
|
||||
object: { type: 'Identifier', name: 'foo' },
|
||||
property: { type: 'Identifier', name: 'bar' },
|
||||
computed: false
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2390,6 +2390,45 @@ describe('Scope', function() {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should allow recursive $emit/$broadcast', inject(function($rootScope) {
|
||||
var callCount = 0;
|
||||
$rootScope.$on('evt', function($event, arg0) {
|
||||
callCount++;
|
||||
if (arg0 !== 1234) {
|
||||
$rootScope.$emit('evt', 1234);
|
||||
$rootScope.$broadcast('evt', 1234);
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$emit('evt');
|
||||
$rootScope.$broadcast('evt');
|
||||
expect(callCount).toBe(6);
|
||||
}));
|
||||
|
||||
|
||||
it('should allow recursive $emit/$broadcast between parent/child', inject(function($rootScope) {
|
||||
var child = $rootScope.$new();
|
||||
var calls = '';
|
||||
|
||||
$rootScope.$on('evt', function($event, arg0) {
|
||||
calls += 'r'; // For "root".
|
||||
if (arg0 === 'fromChild') {
|
||||
$rootScope.$broadcast('evt', 'fromRoot2');
|
||||
}
|
||||
});
|
||||
|
||||
child.$on('evt', function($event, arg0) {
|
||||
calls += 'c'; // For "child".
|
||||
if (arg0 === 'fromRoot1') {
|
||||
child.$emit('evt', 'fromChild');
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$broadcast('evt', 'fromRoot1');
|
||||
expect(calls).toBe('rccrrc');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('doc examples', function() {
|
||||
|
||||
Reference in New Issue
Block a user