Skip to content
This repository was archived by the owner on Mar 31, 2019. It is now read-only.

Commit 83cb935

Browse files
author
Hidekazu Kobayashi
committed
new feature "default child state for abstract state"
1 parent 9837c09 commit 83cb935

File tree

3 files changed

+105
-47
lines changed

3 files changed

+105
-47
lines changed

release/angular-ui-router.js

Lines changed: 96 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* State-based routing for AngularJS
3-
* @version v0.2.11
3+
* @version v0.2.11-dev-2014-09-29
44
* @link http://angular-ui.github.com/
55
* @license MIT License, http://www.opensource.org/licenses/MIT
66
*/
@@ -285,6 +285,7 @@ function $Resolve( $q, $injector) {
285285
*/
286286
this.study = function (invocables) {
287287
if (!isObject(invocables)) throw new Error("'invocables' must be an object");
288+
var invocableKeys = Object.keys(invocables || {});
288289

289290
// Perform a topological sort of invocables to build an ordered plan
290291
var plan = [], cycle = [], visited = {};
@@ -345,7 +346,7 @@ function $Resolve( $q, $injector) {
345346
if (!--wait) {
346347
if (!merged) merge(values, parent.$$values);
347348
result.$$values = values;
348-
result.$$promises = true; // keep for isResolve()
349+
result.$$promises = result.$$promises || true; // keep for isResolve()
349350
delete result.$$inheritedValues;
350351
resolution.resolve(values);
351352
}
@@ -355,28 +356,36 @@ function $Resolve( $q, $injector) {
355356
result.$$failure = reason;
356357
resolution.reject(reason);
357358
}
358-
359+
360+
// TODO: Remove this when we merge in 'new' branch
361+
function omit(obj) {
362+
var copy = {}, keys = angular.isArray(arguments[1]) ? arguments[1] : arguments.slice(1);
363+
for (var key in obj)
364+
if (keys.indexOf(key) == -1) copy[key] = obj[key];
365+
return copy;
366+
}
367+
359368
// Short-circuit if parent has already failed
360369
if (isDefined(parent.$$failure)) {
361370
fail(parent.$$failure);
362371
return result;
363372
}
364373

365374
if (parent.$$inheritedValues) {
366-
merge(values, parent.$$inheritedValues);
375+
merge(values, omit(parent.$$inheritedValues, invocableKeys));
367376
}
368377

369378
// Merge parent values if the parent has already resolved, or merge
370379
// parent promises and wait if the parent resolve is still in progress.
380+
extend(promises, parent.$$promises);
371381
if (parent.$$values) {
372-
merged = merge(values, parent.$$values);
373-
result.$$inheritedValues = parent.$$values;
382+
merged = merge(values, omit(parent.$$values, invocableKeys));
383+
result.$$inheritedValues = omit(parent.$$values, invocableKeys);
374384
done();
375385
} else {
376386
if (parent.$$inheritedValues) {
377-
result.$$inheritedValues = parent.$$inheritedValues;
387+
result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
378388
}
379-
extend(promises, parent.$$promises);
380389
parent.then(done, fail);
381390
}
382391

@@ -579,7 +588,7 @@ function $TemplateFactory( $http, $templateCache, $injector) {
579588
if (isFunction(url)) url = url(params);
580589
if (url == null) return null;
581590
else return $http
582-
.get(url, { cache: $templateCache })
591+
.get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
583592
.then(function(response) { return response.data; });
584593
};
585594

@@ -917,7 +926,7 @@ UrlMatcher.prototype.format = function (values) {
917926
value = values[param];
918927
cfg = this.params[param];
919928

920-
if (!isDefined(value) && (segments[i] === '/' || segments[i + 1] === '/')) continue;
929+
if (!isDefined(value) && (segments[i] === '/' && segments[i + 1] === '/')) continue;
921930
if (value != null) result += encodeURIComponent(cfg.type.encode(value));
922931
result += segments[i + 1];
923932
}
@@ -934,7 +943,7 @@ UrlMatcher.prototype.format = function (values) {
934943
result += (search ? '&' : '?') + param + '=' + (array ? value : encodeURIComponent(value));
935944
search = true;
936945
}
937-
return result;
946+
return result.replace('//', '/');
938947
};
939948

940949
UrlMatcher.prototype.$types = {};
@@ -1710,6 +1719,10 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
17101719
if (!urlMatcher.validates(params)) return null;
17111720

17121721
var isHtml5 = $locationProvider.html5Mode();
1722+
if (angular.isObject(isHtml5)) {
1723+
isHtml5 = isHtml5.enabled;
1724+
}
1725+
17131726
var url = urlMatcher.format(params);
17141727
options = options || {};
17151728

@@ -1872,6 +1885,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
18721885

18731886
if (path) {
18741887
if (!base) throw new Error("No reference point given for path '" + name + "'");
1888+
base = findState(base);
1889+
18751890
var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
18761891

18771892
for (; i < pathLength; i++) {
@@ -2379,9 +2394,12 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
23792394
* reload: true, inherit: false, notify: false
23802395
* });
23812396
* </pre>
2397+
*
2398+
* @returns {promise} A promise representing the state of the new transition. See
2399+
* {@link ui.router.state.$state#methods_go $state.go}.
23822400
*/
23832401
$state.reload = function reload() {
2384-
$state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false });
2402+
return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false });
23852403
};
23862404

23872405
/**
@@ -2521,7 +2539,13 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
25212539
throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
25222540
}
25232541
}
2524-
if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
2542+
var abstract = toState[abstractKey];
2543+
if (typeof abstract === 'string') {
2544+
return $state.transitionTo(abstract. toParams, options);
2545+
} else if (abstract === true) {
2546+
throw new Error("Cannot transition to abstract state '" + to + "'");
2547+
}
2548+
25252549
if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
25262550
to = toState;
25272551

@@ -2705,8 +2729,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
27052729
*
27062730
* @description
27072731
* Similar to {@link ui.router.state.$state#methods_includes $state.includes},
2708-
* but only checks for the full state name. If params is supplied then it will be
2709-
* tested for strict equality against the current active params object, so all params
2732+
* but only checks for the full state name. If params is supplied then it will be
2733+
* tested for strict equality against the current active params object, so all params
27102734
* must match with none missing and no extras.
27112735
*
27122736
* @example
@@ -2722,13 +2746,19 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
27222746
* <div ng-class="{highlighted: $state.is('.item')}">Item</div>
27232747
* </pre>
27242748
*
2725-
* @param {string|object} stateName The state name (absolute or relative) or state object you'd like to check.
2726-
* @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
2749+
* @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
2750+
* @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
27272751
* to test against the current active state.
2752+
* @param {object=} options An options object. The options are:
2753+
*
2754+
* - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will
2755+
* test relative to `options.relative` state (or name).
2756+
*
27282757
* @returns {boolean} Returns true if it is the state.
27292758
*/
2730-
$state.is = function is(stateOrName, params) {
2731-
var state = findState(stateOrName);
2759+
$state.is = function is(stateOrName, params, options) {
2760+
options = extend({ relative: $state.$current }, options || {});
2761+
var state = findState(stateOrName, options.relative);
27322762

27332763
if (!isDefined(state)) {
27342764
return undefined;
@@ -2783,19 +2813,25 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
27832813
*
27842814
* @param {string} stateOrName A partial name, relative name, or glob pattern
27852815
* to be searched for within the current state name.
2786-
* @param {object} params A param object, e.g. `{sectionId: section.id}`,
2816+
* @param {object=} params A param object, e.g. `{sectionId: section.id}`,
27872817
* that you'd like to test against the current active state.
2818+
* @param {object=} options An options object. The options are:
2819+
*
2820+
* - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set,
2821+
* .includes will test relative to `options.relative` state (or name).
2822+
*
27882823
* @returns {boolean} Returns true if it does include the state
27892824
*/
2790-
$state.includes = function includes(stateOrName, params) {
2825+
$state.includes = function includes(stateOrName, params, options) {
2826+
options = extend({ relative: $state.$current }, options || {});
27912827
if (isString(stateOrName) && isGlob(stateOrName)) {
27922828
if (!doesStateMatchGlob(stateOrName)) {
27932829
return false;
27942830
}
27952831
stateOrName = $state.$current.name;
27962832
}
2797-
var state = findState(stateOrName);
27982833

2834+
var state = findState(stateOrName, options.relative);
27992835
if (!isDefined(state)) {
28002836
return undefined;
28012837
}
@@ -2848,7 +2884,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
28482884

28492885
var nav = (state && options.lossy) ? state.navigable : state;
28502886

2851-
if (!nav || !nav.url) {
2887+
if (!nav || nav.url === undefined || nav.url === null) {
28522888
return null;
28532889
}
28542890
return $urlRouter.href(nav.url, filterByKeys(objectKeys(state.params), params || {}), {
@@ -2864,13 +2900,14 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
28642900
* @description
28652901
* Returns the state configuration object for any specific state or all states.
28662902
*
2867-
* @param {string|Sbject=} stateOrName (absolute or relative) If provided, will only get the config for
2903+
* @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
28682904
* the requested state. If not provided, returns an array of ALL state configs.
2905+
* @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
28692906
* @returns {Object|Array} State configuration object or array of all objects.
28702907
*/
28712908
$state.get = function (stateOrName, context) {
28722909
if (arguments.length === 0) return objectKeys(states).map(function(name) { return states[name].self; });
2873-
var state = findState(stateOrName, context);
2910+
var state = findState(stateOrName, context || $state.$current);
28742911
return (state && state.self) ? state.self : null;
28752912
};
28762913

@@ -3075,7 +3112,7 @@ angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider)
30753112
* @description
30763113
* The ui-view directive tells $state where to place your templates.
30773114
*
3078-
* @param {string=} ui-view A view name. The name should be unique amongst the other views in the
3115+
* @param {string=} name A view name. The name should be unique amongst the other views in the
30793116
* same state. You can have views of the same name that live in different states.
30803117
*
30813118
* @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
@@ -3172,8 +3209,8 @@ angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider)
31723209
* <ui-view autoscroll='scopeVariable'/>
31733210
* </pre>
31743211
*/
3175-
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll'];
3176-
function $ViewDirective( $state, $injector, $uiViewScroll) {
3212+
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
3213+
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
31773214

31783215
function getService() {
31793216
return ($injector.has) ? function(service) {
@@ -3203,8 +3240,14 @@ function $ViewDirective( $state, $injector, $uiViewScroll) {
32033240

32043241
if ($animate) {
32053242
return {
3206-
enter: function(element, target, cb) { $animate.enter(element, null, target, cb); },
3207-
leave: function(element, cb) { $animate.leave(element, cb); }
3243+
enter: function(element, target, cb) {
3244+
var promise = $animate.enter(element, null, target, cb);
3245+
if (promise && promise.then) promise.then(cb);
3246+
},
3247+
leave: function(element, cb) {
3248+
var promise = $animate.leave(element, cb);
3249+
if (promise && promise.then) promise.then(cb);
3250+
}
32083251
};
32093252
}
32103253

@@ -3264,7 +3307,7 @@ function $ViewDirective( $state, $injector, $uiViewScroll) {
32643307

32653308
function updateView(firstTime) {
32663309
var newScope,
3267-
name = getUiViewName(attrs, $element.inheritedData('$uiView')),
3310+
name = getUiViewName(scope, attrs, $element, $interpolate),
32683311
previousLocals = name && $state.$current && $state.$current.locals[name];
32693312

32703313
if (!firstTime && previousLocals === latestLocals) return; // nothing to do
@@ -3273,6 +3316,10 @@ function $ViewDirective( $state, $injector, $uiViewScroll) {
32733316

32743317
var clone = $transclude(newScope, function(clone) {
32753318
renderer.enter(clone, $element, function onUiViewEnter() {
3319+
if(currentScope) {
3320+
currentScope.$emit('$viewContentAnimationEnded');
3321+
}
3322+
32763323
if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
32773324
$uiViewScroll(clone);
32783325
}
@@ -3302,16 +3349,16 @@ function $ViewDirective( $state, $injector, $uiViewScroll) {
33023349
return directive;
33033350
}
33043351

3305-
$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state'];
3306-
function $ViewDirectiveFill ($compile, $controller, $state) {
3352+
$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
3353+
function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
33073354
return {
33083355
restrict: 'ECA',
33093356
priority: -400,
33103357
compile: function (tElement) {
33113358
var initial = tElement.html();
33123359
return function (scope, $element, attrs) {
33133360
var current = $state.$current,
3314-
name = getUiViewName(attrs, $element.inheritedData('$uiView')),
3361+
name = getUiViewName(scope, attrs, $element, $interpolate),
33153362
locals = current && current.locals[name];
33163363

33173364
if (! locals) {
@@ -3341,10 +3388,11 @@ function $ViewDirectiveFill ($compile, $controller, $state) {
33413388

33423389
/**
33433390
* Shared ui-view code for both directives:
3344-
* Given attributes and inherited $uiView data, return the view's name
3391+
* Given scope, element, and its attributes, return the view's name
33453392
*/
3346-
function getUiViewName(attrs, inherited) {
3347-
var name = attrs.uiView || attrs.name || '';
3393+
function getUiViewName(scope, attrs, element, $interpolate) {
3394+
var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
3395+
var inherited = element.inheritedData('$uiView');
33483396
return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
33493397
}
33503398

@@ -3439,6 +3487,7 @@ function $StateRefDirective($state, $timeout) {
34393487
link: function(scope, element, attrs, uiSrefActive) {
34403488
var ref = parseStateRef(attrs.uiSref, $state.current.name);
34413489
var params = null, url = null, base = stateContext(element) || $state.$current;
3490+
var newHref = null, isAnchor = element.prop("tagName") === "A";
34423491
var isForm = element[0].nodeName === "FORM";
34433492
var attr = isForm ? "action" : "href", nav = true;
34443493

@@ -3452,10 +3501,10 @@ function $StateRefDirective($state, $timeout) {
34523501
});
34533502

34543503
var update = function(newVal) {
3455-
if (newVal) params = newVal;
3504+
if (newVal) params = angular.copy(newVal);
34563505
if (!nav) return;
34573506

3458-
var newHref = $state.href(ref.state, params, options);
3507+
newHref = $state.href(ref.state, params, options);
34593508

34603509
var activeDirective = uiSrefActive[1] || uiSrefActive[0];
34613510
if (activeDirective) {
@@ -3465,14 +3514,14 @@ function $StateRefDirective($state, $timeout) {
34653514
nav = false;
34663515
return false;
34673516
}
3468-
element[0][attr] = newHref;
3517+
attrs.$set(attr, newHref);
34693518
};
34703519

34713520
if (ref.paramExpr) {
34723521
scope.$watch(ref.paramExpr, function(newVal, oldVal) {
34733522
if (newVal !== params) update(newVal);
34743523
}, true);
3475-
params = scope.$eval(ref.paramExpr);
3524+
params = angular.copy(scope.$eval(ref.paramExpr));
34763525
}
34773526
update();
34783527

@@ -3487,8 +3536,11 @@ function $StateRefDirective($state, $timeout) {
34873536
});
34883537
e.preventDefault();
34893538

3539+
// if the state has no URL, ignore one preventDefault from the <a> directive.
3540+
var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
34903541
e.preventDefault = function() {
3491-
$timeout.cancel(transition);
3542+
if (ignorePreventDefaultCount-- <= 0)
3543+
$timeout.cancel(transition);
34923544
};
34933545
}
34943546
});
@@ -3604,7 +3656,7 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {
36043656
if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
36053657
return $state.$current.self === state && matchesParams();
36063658
} else {
3607-
return $state.includes(state.name) && matchesParams();
3659+
return state && $state.includes(state.name) && matchesParams();
36083660
}
36093661
}
36103662

@@ -3655,4 +3707,4 @@ function $IncludedByStateFilter($state) {
36553707
angular.module('ui.router.state')
36563708
.filter('isState', $IsStateFilter)
36573709
.filter('includedByState', $IncludedByStateFilter);
3658-
})(window, window.angular);
3710+
})(window, window.angular);

0 commit comments

Comments
 (0)