From 322c85bf8bdf39ad785ea919820ebb6591925a96 Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 10 Sep 2013 14:42:31 -0500
Subject: [PATCH 01/21] add flag in .transitionTo() to allow ability to disable
broadcast of $stateChangeSuccess
---
src/state.js | 8 ++++++--
test/stateSpec.js | 26 ++++++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/src/state.js b/src/state.js
index 8020876a6..9e5085f78 100644
--- a/src/state.js
+++ b/src/state.js
@@ -215,7 +215,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
$state.transitionTo = function transitionTo(to, toParams, options) {
if (!isDefined(options)) options = (options === true || options === false) ? { location: options } : {};
toParams = toParams || {};
- options = extend({ location: true, inherit: false, relative: null }, options);
+ options = extend({ location: true, inherit: false, relative: null, broadcastStateChangeSuccess : true }, options);
var toState = findState(to, options.relative);
@@ -274,6 +274,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
var transition = $state.transition = resolved.then(function () {
var l, entering, exiting;
+
if ($state.transition !== transition) return TransitionSuperseded;
// Exit 'from' states not kept
@@ -307,7 +308,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
$location.url(toNav.url.format(toNav.locals.globals.$stateParams));
}
- $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
+ if(options.broadcastStateChangeSuccess){
+ $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
+ }
+
return $state.current;
}, function (error) {
diff --git a/test/stateSpec.js b/test/stateSpec.js
index 02b180a96..cc76e6cc8 100644
--- a/test/stateSpec.js
+++ b/test/stateSpec.js
@@ -157,6 +157,32 @@ describe('state', function () {
expect($state.current).toBe(D);
}));
+
+ it('does not trigger $stateChangeSuccess when flag prevents, but still transitions when different state', inject(function ($state, $q, $rootScope) {
+ initStateTo(E, { i: 'iii' });
+ var called;
+ $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
+ called = true;
+ });
+ $state.transitionTo(D, { x: '1', y: '2' }, {broadcastStateChangeSuccess : false});
+ $q.flush();
+ expect(called).toBeFalsy();
+ expect($state.current).toBe(D);
+ }));
+
+ it('does not trigger $stateChangeSuccess when flag prevents, yet still updates params', inject(function ($state, $q, $rootScope) {
+ initStateTo(E, { x: 'iii' });
+ var called;
+ $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
+ called = true;
+ });
+ $state.transitionTo(E, { i: '1', y: '2' }, {broadcastStateChangeSuccess : false});
+ $q.flush();
+ expect(called).toBeFalsy();
+ expect($state.params.i).toBe('1');
+ expect($state.current).toBe(E);
+ }));
+
it('is a no-op when passing the current state and identical parameters', inject(function ($state, $q) {
initStateTo(A);
var trans = $state.transitionTo(A, {}); // no-op
From 8518db23edb0cf1b7b818457ce176fde3ffc6a13 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Thu, 19 Sep 2013 09:53:10 -0500
Subject: [PATCH 02/21] can register a type
---
sample/states.js | 15 +++++++++++++++
src/state.js | 7 +++++++
src/urlMatcherFactory.js | 12 ++++++++++++
test/urlMatcherFactorySpec.js | 9 +++++++++
4 files changed, 43 insertions(+)
diff --git a/sample/states.js b/sample/states.js
index 45e57dd83..bc78f6ee3 100644
--- a/sample/states.js
+++ b/sample/states.js
@@ -27,6 +27,21 @@ angular.module('uiRouterSample')
// Use $stateProvider to configure your states.
$stateProvider
+ /////////////////////
+ // Parameter Types //
+ /////////////////////
+ .registerType("date", {
+ equals: function (typeObj, otherObj) {
+ return typeObj.toISOString() === otherObj.toISOString();
+ },
+ decode: function (typeObj) {
+ return typeObj.toISOString();
+ },
+ encode: function (value) {
+ return new Date(value);
+ }
+ })
+
//////////
// Home //
//////////
diff --git a/src/state.js b/src/state.js
index beb9f8f05..a88f7afbd 100644
--- a/src/state.js
+++ b/src/state.js
@@ -107,6 +107,13 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
$delegates: {}
};
+ // Create a proxy through to the Type registration on the UrlMatcherFactory
+ this.isTypeRegistered = $urlMatcherFactory.isTypeRegistered;
+ this.registerType = function (name, handler) {
+ $urlMatcherFactory.registerType(name, handler);
+ return this;
+ }
+
function isRelative(stateName) {
return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
}
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index ec78c522a..6b4f04a12 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -247,6 +247,18 @@ function $UrlMatcherFactory() {
return isObject(o) && isFunction(o.exec) && isFunction(o.format) && isFunction(o.concat);
};
+ var typeRegistrar = {};
+ this.registerType = function (name, handler) {
+ if (!isString(name) || !isObject(handler) || !isFunction(handler.equals) || !isFunction(handler.decode) || !isFunction(handler.encode)) {
+ throw new Error("Invalid type '" + name + "'");
+ }
+ typeRegistrar[name] = handler;
+ };
+
+ this.isTypeRegistered = function (name) {
+ return isDefined(typeRegistrar[name]);
+ };
+
this.$get = function () {
return this;
};
diff --git a/test/urlMatcherFactorySpec.js b/test/urlMatcherFactorySpec.js
index b16ea4dbb..259fdc2ee 100644
--- a/test/urlMatcherFactorySpec.js
+++ b/test/urlMatcherFactorySpec.js
@@ -132,4 +132,13 @@ describe("urlMatcherFactory", function () {
it("recognizes matchers", function () {
expect($umf.isMatcher(new UrlMatcher('/'))).toBe(true);
});
+
+ it("registers types", function () {
+ $umf.registerType("test", {
+ equals: function (typeObj, otherObj) {},
+ decode: function (typeObj) {},
+ encode: function (value) {}
+ });
+ expect($umf.isTypeRegistered("test")).toBe(true);
+ });
});
From 5220d5c0661fd1a9d873c44f70b2d67989852bb0 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Thu, 19 Sep 2013 11:41:32 -0500
Subject: [PATCH 03/21] UrlMatcher looks for types in the Url and generates a
typeMap between params and known types. Exec will run any found types through
the decode function
---
src/urlMatcherFactory.js | 50 ++++++++++++++++++++++++++---------
test/urlMatcherFactorySpec.js | 23 ++++++++++++++--
2 files changed, 59 insertions(+), 14 deletions(-)
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 6b4f04a12..9fcdb87f5 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -59,7 +59,8 @@ function UrlMatcher(pattern) {
var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
names = {}, compiled = '^', last = 0, m,
segments = this.segments = [],
- params = this.params = [];
+ params = this.params = [],
+ typeMap = this.typeMap = {};
function addParameter(id) {
if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
@@ -80,6 +81,10 @@ function UrlMatcher(pattern) {
while ((m = placeholder.exec(pattern))) {
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
+ if (isDefined(this.types[regexp])) {
+ this.typeMap[id] = regexp;
+ regexp = '[^/]*';
+ }
segment = pattern.substring(last, m.index);
if (segment.indexOf('?') >= 0) break; // we're into the search part
compiled += quoteRegExp(segment) + '(' + regexp + ')';
@@ -154,7 +159,10 @@ UrlMatcher.prototype.toString = function () {
* @return {Object} The captured parameter values.
*/
UrlMatcher.prototype.exec = function (path, searchParams) {
- var m = this.regexp.exec(path);
+ var m = this.regexp.exec(path),
+ types = this.types,
+ typeMap = this.typeMap;
+
if (!m) return null;
var params = this.params, nTotal = params.length,
@@ -166,6 +174,12 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
for (i=0; i
Date: Fri, 20 Sep 2013 14:23:43 -0500
Subject: [PATCH 04/21] format decodes typed values. removed normalization that
was forcing back to strings
---
sample/states.js | 4 +--
src/state.js | 9 ++++---
src/urlMatcherFactory.js | 46 ++++++++++++++++++++++++++++++-----
test/stateDirectivesSpec.js | 12 ++++-----
test/stateSpec.js | 10 ++++----
test/urlMatcherFactorySpec.js | 28 +++++++++++++++------
6 files changed, 78 insertions(+), 31 deletions(-)
diff --git a/sample/states.js b/sample/states.js
index bc78f6ee3..94d6e7261 100644
--- a/sample/states.js
+++ b/sample/states.js
@@ -30,7 +30,7 @@ angular.module('uiRouterSample')
/////////////////////
// Parameter Types //
/////////////////////
- .registerType("date", {
+ .type("date", {
equals: function (typeObj, otherObj) {
return typeObj.toISOString() === otherObj.toISOString();
},
@@ -154,7 +154,7 @@ angular.module('uiRouterSample')
// So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the
// url becomes something like '/contacts/42' then this state becomes active
// and the $stateParams object becomes { contactId: 42 }.
- url: '/{contactId:[0-9]{1,4}}',
+ url: '/{contactId:integer}',
// If there is more than a single ui-view in the parent template, or you would
// like to target a ui-view from even higher up the state tree, you can use the
diff --git a/src/state.js b/src/state.js
index a88f7afbd..1c416ea0c 100644
--- a/src/state.js
+++ b/src/state.js
@@ -109,10 +109,10 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
// Create a proxy through to the Type registration on the UrlMatcherFactory
this.isTypeRegistered = $urlMatcherFactory.isTypeRegistered;
- this.registerType = function (name, handler) {
- $urlMatcherFactory.registerType(name, handler);
+ this.type = function (name, handler) {
+ $urlMatcherFactory.type(name, handler);
return this;
- }
+ };
function isRelative(stateName) {
return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
@@ -463,7 +463,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
forEach(keys, function (name) {
var value = values[name];
- normalized[name] = (value != null) ? String(value) : null;
+ //normalized[name] = (value != null) ? String(value) : null;
+ normalized[name] = (value != null) ? value : null;
});
return normalized;
}
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 9fcdb87f5..c98f5921b 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -174,13 +174,17 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
for (i=0; i
Date: Fri, 20 Sep 2013 20:22:37 -0400
Subject: [PATCH 05/21] Correctly detect left-click on IE8, fixes #452.
---
src/stateDirectives.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/stateDirectives.js b/src/stateDirectives.js
index da0c64668..00a57a580 100644
--- a/src/stateDirectives.js
+++ b/src/stateDirectives.js
@@ -44,7 +44,9 @@ function $StateRefDirective($state) {
if (isForm) return;
element.bind("click", function(e) {
- if ((e.which == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
+ var button = e.which || e.button;
+
+ if ((button == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
$state.go(ref.state, params, { relative: base });
scope.$apply();
e.preventDefault();
From 4cdadcf46875698aee6c3684cc32f2a0ce553c45 Mon Sep 17 00:00:00 2001
From: Nate Abele
Date: Fri, 20 Sep 2013 22:41:57 -0400
Subject: [PATCH 06/21] fix($resolve): resolve factories from string names
$resolve now correctly executes factory functions when provided the name of the factory as a string.
Closes #449
---
src/resolve.js | 2 +-
test/resolveSpec.js | 15 +++++++++++++++
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/resolve.js b/src/resolve.js
index 4f1a4cfcf..45cf6cf88 100644
--- a/src/resolve.js
+++ b/src/resolve.js
@@ -42,7 +42,7 @@ function $Resolve( $q, $injector) {
visited[key] = VISIT_IN_PROGRESS;
if (isString(value)) {
- plan.push(key, [ function() { return $injector.get(key); }], NO_DEPENDENCIES);
+ plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
} else {
var params = $injector.annotate(value);
forEach(params, function (param) {
diff --git a/test/resolveSpec.js b/test/resolveSpec.js
index d5c7d745d..fda9e0ecc 100644
--- a/test/resolveSpec.js
+++ b/test/resolveSpec.js
@@ -3,6 +3,12 @@ describe("resolve", function () {
var $r, tick;
beforeEach(module('ui.router.util'));
+ beforeEach(module(function($provide) {
+ $provide.factory('Foo', function() {
+ return "Working";
+ });
+ }));
+
beforeEach(inject(function($resolve, $q) {
$r = $resolve;
tick = $q.flush;
@@ -292,6 +298,15 @@ describe("resolve", function () {
r({ what: 'hi' });
expect(trace).toEqual([ 'a: 1', 'a: hi' ]);
});
+
+ it("resolves values from string factory names", function () {
+ var result, r = $r.study({ foo: "Foo" })().then(function(values) {
+ result = values['foo'];
+ });
+ tick();
+
+ expect(result).toBe("Working");
+ });
});
});
From d3c6a5dea073b104b758c5d119660eea05e4628d Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Mon, 23 Sep 2013 11:50:19 -0500
Subject: [PATCH 07/21] added is and normalization to type
---
src/state.js | 1 -
src/urlMatcherFactory.js | 46 +++++++++++++++++++++++++++++---
test/urlMatcherFactorySpec.js | 49 +++++++++++++++++++++++++++++++----
3 files changed, 87 insertions(+), 9 deletions(-)
diff --git a/src/state.js b/src/state.js
index 1c416ea0c..a1061bfa8 100644
--- a/src/state.js
+++ b/src/state.js
@@ -108,7 +108,6 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
};
// Create a proxy through to the Type registration on the UrlMatcherFactory
- this.isTypeRegistered = $urlMatcherFactory.isTypeRegistered;
this.type = function (name, handler) {
$urlMatcherFactory.type(name, handler);
return this;
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index c98f5921b..9eb16b7d7 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -249,8 +249,14 @@ UrlMatcher.prototype.format = function (values) {
UrlMatcher.prototype.types = {
'boolean': {
+ is: function (typeObj) {
+ return (typeObj === true || typeObj === false);
+ },
equals: function (typeObj, otherObj) {
- return typeObj === otherObj;
+ if (this.is(typeObj) && this.is(otherObj)) {
+ return typeObj === otherObj;
+ }
+ return false;
},
encode: function (typeObj) {
return typeObj.toString();
@@ -262,8 +268,15 @@ UrlMatcher.prototype.types = {
}
},
'integer': {
+ is: function (typeObj) {
+ var regexp = /^\d+$/;
+ return !!regexp.exec(this.encode(typeObj));
+ },
equals: function (typeObj, otherObj) {
- return typeObj === otherObj;
+ if (this.is(typeObj) && this.is(otherObj)) {
+ return typeObj === otherObj;
+ }
+ return false;
},
encode: function (typeObj) {
return typeObj.toString();
@@ -278,9 +291,36 @@ UrlMatcher.prototype.types = {
TODO
*/
UrlMatcher.prototype.type = function (name, handler) {
- if (!isString(name) || !isObject(handler) || !isFunction(handler.equals) || !isFunction(handler.decode) || !isFunction(handler.encode)) {
+ // return the handle if only the name was provided
+ if (!handler && UrlMatcher.prototype.types[name]) {
+ return UrlMatcher.prototype.types[name];
+ }
+
+ if (!isString(name) || !isObject(handler) || !isFunction(handler.decode) || !isFunction(handler.encode)) {
throw new Error("Invalid type '" + name + "'");
}
+
+ // normalize the handler
+ if (isString(handler.is)) {
+ handler.regexp = new RegExp(handler.is);
+ handler.is = function (typeObj) {
+ return !!handler.regexp.exec(handler.encode(typeObj));
+ };
+ }
+ if (!isFunction(handler.is)) {
+ handler.is = function (typeObj) {
+ return (JSON.stringify(handler.decode(handler.encode(typeObj))) === JSON.stringify(typeObj));
+ };
+ }
+ if (!isFunction(handler.equals)) {
+ handler.equals = function (typeObj, otherObj) {
+ if (handler.is(typeObj) && handler.is(otherObj)) {
+ return handler.encode(typeObj) === handler.encode(otherObj);
+ }
+ return false;
+ };
+ }
+
UrlMatcher.prototype.types[name] = handler;
};
diff --git a/test/urlMatcherFactorySpec.js b/test/urlMatcherFactorySpec.js
index 7cb780ebe..3ffac2a62 100644
--- a/test/urlMatcherFactorySpec.js
+++ b/test/urlMatcherFactorySpec.js
@@ -164,12 +164,51 @@ describe("urlMatcherFactory", function () {
expect($umf.isMatcher(new UrlMatcher('/'))).toBe(true);
});
- it("registers types", function () {
+ it("defines builtin boolean type", function () {
+ var booleanHandler = $umf.type("boolean");
+ expect(booleanHandler.equals(true, true)).toBe(true);
+ expect(booleanHandler.equals(true, "blue")).toBe(false);
+ expect(booleanHandler.is(false)).toBe(true);
+ expect(booleanHandler.is(456)).toBe(false);
+ expect(booleanHandler.encode(false)).toBe("false");
+ expect(booleanHandler.decode("False")).toBe(false);
+ expect(booleanHandler.decode("purple")).toBe(undefined);
+ });
+
+ it("defines builtin integer type", function () {
+ var integerHandler = $umf.type("integer");
+ expect(integerHandler.equals(5, 5)).toBe(true);
+ expect(integerHandler.equals(5, "blue")).toBe(false);
+ expect(integerHandler.is(67)).toBe(true);
+ expect(integerHandler.is(45.2)).toBe(false);
+ expect(integerHandler.is({})).toBe(false);
+ expect(integerHandler.encode(342)).toBe("342");
+ expect(integerHandler.decode("5563")).toBe(5563);
+ });
+
+ it("registers minimal custom types", function () {
$umf.type("test", {
- equals: function (typeObj, otherObj) {},
- decode: function (typeObj) {},
- encode: function (value) {}
+ encode: function (typeObj) { return typeObj.value; },
+ decode: function (value) { return { value: value }; }
});
- expect($umf.compile('/').types["test"]).toBeDefined();
+ var typeHandler = $umf.type("test");
+ expect(typeHandler.equals({ value: "one" }, { value: "one" })).toBe(true);
+ expect(typeHandler.equals({ value: "one" }, { value: "two" })).toBe(false);
+ expect(typeHandler.is({ value: "one" })).toBe(true);
+ expect(typeHandler.is(456)).toBe(false);
+ });
+
+ it("registers complete custom types", function () {
+ $umf.type("test", {
+ encode: function (typeObj) { return typeObj.value; },
+ decode: function (value) { return { value: value }; },
+ is: function (typeObj) { return (isObject(typeObj) && !!typeObj.value); },
+ equals: function (typeObj, otherObj) { return (typeObj.value === otherObj.value && typeObj.value !== undefined); }
+ });
+ var typeHandler = $umf.type("test");
+ expect(typeHandler.equals({ value: "one" }, { value: "one" })).toBe(true);
+ expect(typeHandler.equals({ value: "one" }, { value: "two" })).toBe(false);
+ expect(typeHandler.is({ value: "one" })).toBe(true);
+ expect(typeHandler.is(456)).toBe(false);
});
});
From 0cd78aeee09d5995cc1cb9f00a26530520fa32e5 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Mon, 23 Sep 2013 12:04:16 -0500
Subject: [PATCH 08/21] matcher.exec checks typed parameters are of correct
type
---
src/urlMatcherFactory.js | 12 +++++++++---
test/urlMatcherFactorySpec.js | 26 ++++++++++----------------
2 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 9eb16b7d7..56c8a9ca6 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -174,17 +174,23 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
for (i=0; i
Date: Mon, 23 Sep 2013 13:34:57 -0500
Subject: [PATCH 09/21] split pattern and is
---
src/urlMatcherFactory.js | 28 ++++++++++------------------
test/urlMatcherFactorySpec.js | 21 ++++++++++++++++++---
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 56c8a9ca6..5336eeb90 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -83,7 +83,7 @@ function UrlMatcher(pattern) {
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
if (isDefined(this.types[regexp])) {
this.typeMap[id] = regexp;
- regexp = '[^/]*';
+ regexp = this.types[regexp].pattern; // use the regexp defined for this type instead
}
segment = pattern.substring(last, m.index);
if (segment.indexOf('?') >= 0) break; // we're into the search part
@@ -174,23 +174,17 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
for (i=0; i
Date: Mon, 23 Sep 2013 13:42:53 -0500
Subject: [PATCH 10/21] format checks the type before encoding
---
src/urlMatcherFactory.js | 5 ++++-
test/urlMatcherFactorySpec.js | 2 ++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 5336eeb90..228f88cd7 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -223,7 +223,10 @@ UrlMatcher.prototype.format = function (values) {
var encodedValues = {};
forEach(values, function (value, key) {
if (isDefined(typeMap[key])) {
- encodedValues[key] = types[typeMap[key]].encode(value);
+ var typeHandler = types[typeMap[key]];
+ if (typeHandler.is(value)) {
+ encodedValues[key] = typeHandler.encode(value);
+ }
}
else {
encodedValues[key] = value;
diff --git a/test/urlMatcherFactorySpec.js b/test/urlMatcherFactorySpec.js
index 9d2216c92..30f5c88ac 100644
--- a/test/urlMatcherFactorySpec.js
+++ b/test/urlMatcherFactorySpec.js
@@ -124,6 +124,8 @@ describe("UrlMatcher", function () {
it(".format() encode typed URL parameters", function () {
expect(new UrlMatcher('/users/{id:integer}').format({ id: 55 })).toEqual('/users/55');
+ expect(new UrlMatcher('/users/{id:boolean}').format({ id: false })).toEqual('/users/false');
+ expect(new UrlMatcher('/users/{id:boolean}').format({ id: "something" })).toEqual('/users/');
});
it(".concat() concatenates matchers", function () {
From 308cc1d263afbfccfa3fb132fc7ede23e23986e3 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Mon, 23 Sep 2013 15:30:15 -0500
Subject: [PATCH 11/21] evaluates type equality when transitioning
---
src/state.js | 20 ++++++++++++++------
test/stateSpec.js | 16 +++++++++++++++-
2 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/src/state.js b/src/state.js
index a1061bfa8..8ff07932c 100644
--- a/src/state.js
+++ b/src/state.js
@@ -170,7 +170,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
// Register the state in the global state list and with $urlRouter if necessary.
if (!state['abstract'] && state.url) {
$urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
- if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
+ if ($state.$current.navigable != state || !equalForKeys(state, $match, $stateParams)) {
$state.transitionTo(state, $match, false);
}
}]);
@@ -294,7 +294,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
// Starting from the root of the path, keep all levels that haven't changed
var keep, state, locals = root.locals, toLocals = [];
for (keep = 0, state = toPath[keep];
- state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams);
+ state && state === fromPath[keep] && equalForKeys(state, toParams, fromParams, state.ownParams);
keep++, state = toPath[keep]) {
locals = toLocals[keep] = state.locals;
}
@@ -468,16 +468,24 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $
return normalized;
}
- function equalForKeys(a, b, keys) {
+ function equalForKeys(state, aParams, bParams, keys) {
+ var url = state.url || {},
+ types = url.types || {},
+ typeMap = url.typeMap || {};
+
// If keys not provided, assume keys from object 'a'
if (!keys) {
keys = [];
- for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
+ for (var n in aParams) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
}
for (var i=0; i
Date: Tue, 24 Sep 2013 13:01:56 -0500
Subject: [PATCH 12/21] added sample for custom type. documented the type
function
---
sample/states.js | 27 ++++++++++++++++++++-------
src/urlMatcherFactory.js | 23 ++++++++++++++++++++++-
2 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/sample/states.js b/sample/states.js
index 94d6e7261..1d758e4f9 100644
--- a/sample/states.js
+++ b/sample/states.js
@@ -30,15 +30,27 @@ angular.module('uiRouterSample')
/////////////////////
// Parameter Types //
/////////////////////
- .type("date", {
+ // contact id is formatted like ##-####, as dependent on some backend system.
+ .type("myCustomId", {
+ pattern: "[0-9]{2}[\-][0-9]{4}",
+ is: function (typeObj) {
+ return (angular.isObject(typeObj) && typeObj.firstPart && typeObj.secondPart);
+ },
equals: function (typeObj, otherObj) {
- return typeObj.toISOString() === otherObj.toISOString();
+ if (this.is(typeObj) && this.is(otherObj)) {
+ return (typeObj.firstPart === otherObj.firstPart && typeObj.secondPart === otherObj.secondPart);
+ }
+ return false;
},
- decode: function (typeObj) {
- return typeObj.toISOString();
+ decode: function (value) {
+ var tokens = value.split("-");
+ return {
+ firstPart: tokens[0],
+ secondPart: tokens[1]
+ };
},
- encode: function (value) {
- return new Date(value);
+ encode: function (typeObj) {
+ return typeObj.firstPart + "-" + typeObj.secondPart;
}
})
@@ -154,7 +166,7 @@ angular.module('uiRouterSample')
// So its url will end up being '/contacts/{contactId:[0-9]{1,8}}'. When the
// url becomes something like '/contacts/42' then this state becomes active
// and the $stateParams object becomes { contactId: 42 }.
- url: '/{contactId:integer}',
+ url: '/{contactId:myCustomId}',
// If there is more than a single ui-view in the parent template, or you would
// like to target a ui-view from even higher up the state tree, you can use the
@@ -172,6 +184,7 @@ angular.module('uiRouterSample')
templateUrl: 'contacts.detail.html',
controller: ['$scope', '$stateParams', 'utils',
function ( $scope, $stateParams, utils) {
+ console.log($stateParams.contactId);
$scope.contact = utils.findById($scope.contacts, $stateParams.contactId);
}]
},
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 228f88cd7..1c75ecfce 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -292,7 +292,28 @@ UrlMatcher.prototype.types = {
};
/**
-TODO
+ * Registers a custom type for parameters or gets a handler for a registered type.
+ * A handler object must include a `decode` function that decodes a string value
+ * from the URL into the type, and a `encode` function that encodes the type into
+ * a string value for the URL.
+ *
+ * ### Example
+ * ```
+ * // Register myType
+ * .type('myType', {
+ * pattern: "[0-9]+", // (Optional) Regex pattern used to match the URL to this type.
+ * is : function (typeObj) {}, // (Optional) Determines if a param is of this type when saving to the URL.
+ * equals: function (typeObj, otherObj) {}, // (Optional) Determines if two objects of this type are equal.
+ * encode: function (typeObj) {}, // (Required) Encode this type to the URL.
+ * decode: function (value) {} // (Required) Decode the URL segment to this type.
+ * });
+ * // Get myType
+ * .type('myType');
+ * ```
+ *
+ * @param {string} name the name of the type to register or get.
+ * @param {Object} handler the handler object with functions of working with this type.
+ * @return {Object} the handler object.
*/
UrlMatcher.prototype.type = function (name, handler) {
// return the handle if only the name was provided
From e22517d220d8039eb105be5d582f57d81aa710a7 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:14:44 -0500
Subject: [PATCH 13/21] fixed equality in integer type
---
src/urlMatcherFactory.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js
index 1c75ecfce..c592e8df9 100644
--- a/src/urlMatcherFactory.js
+++ b/src/urlMatcherFactory.js
@@ -274,7 +274,7 @@ UrlMatcher.prototype.types = {
'integer': {
pattern: "[0-9]+",
is: function (typeObj) {
- return typeof typeObj === 'number' && typeObj % 1 == 0;
+ return typeof typeObj === 'number' && typeObj % 1 === 0;
},
equals: function (typeObj, otherObj) {
if (this.is(typeObj) && this.is(otherObj)) {
From bcd71cb4ac0d52dcd841e8ebb5859469fd0ab951 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:33:21 -0500
Subject: [PATCH 14/21] fixing jsdoc for windows
---
Gruntfile.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Gruntfile.js b/Gruntfile.js
index 7af87827b..00f6b4b68 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -106,7 +106,7 @@ module.exports = function (grunt) {
grunt.registerTask('jsdoc', 'Generate documentation', function () {
promising(this,
- system('node_modules/jsdoc/jsdoc -c config/jsdoc.js -d \'' + grunt.config('builddir') + '\'/doc src')
+ system('./node_modules/jsdoc/jsdoc -c ./config/jsdoc.js -d ./\'' + grunt.config('builddir') + '\'/doc src')
);
});
From c9da9f235bade3b75f10db86e431dd483ac17570 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:36:25 -0500
Subject: [PATCH 15/21] fixing jsdoc for windows
---
Gruntfile.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Gruntfile.js b/Gruntfile.js
index 00f6b4b68..33e74ed3a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -106,7 +106,7 @@ module.exports = function (grunt) {
grunt.registerTask('jsdoc', 'Generate documentation', function () {
promising(this,
- system('./node_modules/jsdoc/jsdoc -c ./config/jsdoc.js -d ./\'' + grunt.config('builddir') + '\'/doc src')
+ system('\"./node_modules/jsdoc/jsdoc\" -c ./config/jsdoc.js -d \"./' + grunt.config('builddir') + '/doc\" src')
);
});
From 841239c1c7d15793e280d2e19e093d34ae28f280 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:38:01 -0500
Subject: [PATCH 16/21] fixing release for windows
---
Gruntfile.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Gruntfile.js b/Gruntfile.js
index 33e74ed3a..3cbd82b80 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -150,10 +150,10 @@ module.exports = function (grunt) {
var version = grunt.config('pkg.version'), releasedir = grunt.config('builddir');
promising(this,
- system('git add \'' + releasedir + '\'').then(function () {
+ system('git add \"' + releasedir + '\"').then(function () {
return system('git commit -m \'release ' + version + '\'');
}).then(function () {
- return system('git tag \'' + version + '\'');
+ return system('git tag \"' + version + '\"');
})
);
});
From cf3e5b69c765a79828136e87a4d99ea2fdf51662 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:43:40 -0500
Subject: [PATCH 17/21] for 0.2.1. release
---
bower.json | 2 +-
component.json | 2 +-
package.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/bower.json b/bower.json
index 73b6ff0ee..f19776ced 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "angular-ui-router",
- "version": "0.2.0",
+ "version": "0.2.1",
"main": "./release/angular-ui-router.min.js",
"dependencies": {
"angular": ">= 1.0.6"
diff --git a/component.json b/component.json
index 4f23115ba..151f9576e 100644
--- a/component.json
+++ b/component.json
@@ -1,6 +1,6 @@
{
"name": "angular-ui-router",
- "version": "0.2.0",
+ "version": "0.2.1",
"description": "State-based routing for AngularJS",
"keywords": [
"angular",
diff --git a/package.json b/package.json
index afb8ebf78..3149ad136 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "angular-ui-router",
"description": "State-based routing for AngularJS",
- "version": "0.2.0",
+ "version": "0.2.1",
"homepage": "http://angular-ui.github.com/",
"author": {
"name": "Karsten Sperling",
From 7e96189e5bafd25bcf4b90a7a2a11355babadaa0 Mon Sep 17 00:00:00 2001
From: "Todd H. Gardner"
Date: Tue, 24 Sep 2013 13:45:32 -0500
Subject: [PATCH 18/21] release 0.2.1
---
release/angular-ui-router.js | 317 +++++++++++++++++++++++++---
release/angular-ui-router.min.js | 4 +-
release/doc/$resolve.html | 34 +--
release/doc/$templateFactory.html | 37 +++-
release/doc/$urlMatcherFactory.html | 23 +-
release/doc/UrlMatcher.html | 236 ++++++++++++++++++---
release/doc/global.html | 13 +-
release/doc/index.html | 5 +-
release/doc/scripts/linenumber.js | 17 ++
9 files changed, 599 insertions(+), 87 deletions(-)
create mode 100644 release/doc/scripts/linenumber.js
diff --git a/release/angular-ui-router.js b/release/angular-ui-router.js
index feb17c7ce..db2cf24b9 100644
--- a/release/angular-ui-router.js
+++ b/release/angular-ui-router.js
@@ -1,9 +1,15 @@
/**
* State-based routing for AngularJS
- * @version v0.2.0
+ * @version v0.2.1
* @link http://angular-ui.github.com/
* @license MIT License, http://www.opensource.org/licenses/MIT
*/
+
+/* commonjs package manager support (eg componentjs) */
+if (module && exports && module.exports === exports){
+ module.exports = 'ui.router';
+}
+
(function (window, angular, undefined) {
/*jshint globalstrict:true*/
/*global angular:false*/
@@ -127,7 +133,7 @@ function $Resolve( $q, $injector) {
visited[key] = VISIT_IN_PROGRESS;
if (isString(value)) {
- plan.push(key, [ function() { return $injector.get(key); }], NO_DEPENDENCIES);
+ plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
} else {
var params = $injector.annotate(value);
forEach(params, function (param) {
@@ -214,7 +220,7 @@ function $Resolve( $q, $injector) {
}
// Wait for any parameter that we have a promise for (either from parent or from this
// resolve; in that case study() will have made sure it's ordered before us in the plan).
- params.forEach(function (dep) {
+ forEach(params, function (dep) {
if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
waitParams++;
promises[dep].then(function (result) {
@@ -448,7 +454,8 @@ function UrlMatcher(pattern) {
var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
names = {}, compiled = '^', last = 0, m,
segments = this.segments = [],
- params = this.params = [];
+ params = this.params = [],
+ typeMap = this.typeMap = {};
function addParameter(id) {
if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
@@ -469,6 +476,10 @@ function UrlMatcher(pattern) {
while ((m = placeholder.exec(pattern))) {
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
+ if (isDefined(this.types[regexp])) {
+ this.typeMap[id] = regexp;
+ regexp = this.types[regexp].pattern; // use the regexp defined for this type instead
+ }
segment = pattern.substring(last, m.index);
if (segment.indexOf('?') >= 0) break; // we're into the search part
compiled += quoteRegExp(segment) + '(' + regexp + ')';
@@ -543,7 +554,10 @@ UrlMatcher.prototype.toString = function () {
* @return {Object} The captured parameter values.
*/
UrlMatcher.prototype.exec = function (path, searchParams) {
- var m = this.regexp.exec(path);
+ var m = this.regexp.exec(path),
+ types = this.types,
+ typeMap = this.typeMap;
+
if (!m) return null;
var params = this.params, nTotal = params.length,
@@ -555,7 +569,17 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
for (i=0; i=0||(s.push(a[c]),u[a[c]]=r[a[c]])}return P({},u,t)}function u(r,t){var n=1,o=2,i={},u=[],s=i,l=P(r.when(i),{$$promises:i,$$values:i});this.study=function(i){function c(r,e){if(v[e]!==o){if(p.push(e),v[e]===n)throw p.splice(0,p.indexOf(e)),Error("Cyclic dependency: "+p.join(" -> "));if(v[e]=n,b(r))h.push(e,[function(){return t.get(e)}],u);else{var a=t.annotate(r);y(a,function(r){r!==e&&i.hasOwnProperty(r)&&c(i[r],r)}),h.push(e,r,a)}p.pop(),v[e]=o}}function f(r){return E(r)&&r.then&&r.$$promises}if(!E(i))throw Error("'invocables' must be an object");var h=[],p=[],v={};return y(i,c),i=p=v=null,function(n,o,i){function u(){--w||(b||a(d,o.$$values),$.$$values=d,$.$$promises=!0,v.resolve(d))}function c(r){$.$$failure=r,v.reject(r)}function p(e,a,o){function s(r){f.reject(r),c(r)}function l(){if(!g($.$$failure))try{f.resolve(t.invoke(a,i,d)),f.promise.then(function(r){d[e]=r,u()},s)}catch(r){s(r)}}var f=r.defer(),h=0;o.forEach(function(r){m.hasOwnProperty(r)&&!n.hasOwnProperty(r)&&(h++,m[r].then(function(t){d[r]=t,--h||l()},s))}),h||l(),m[e]=f.promise}if(f(n)&&i===e&&(i=o,o=n,n=null),n){if(!E(n))throw Error("'locals' must be an object")}else n=s;if(o){if(!f(o))throw Error("'parent' must be a promise returned by $resolve.resolve()")}else o=l;var v=r.defer(),$=v.promise,m=$.$$promises={},d=P({},n),w=1+h.length/3,b=!1;if(g(o.$$failure))return c(o.$$failure),$;o.$$values?(b=a(d,o.$$values),u()):(P(m,o.$$promises),o.then(u,c));for(var x=0,y=h.length;y>x;x+=3)n.hasOwnProperty(h[x])?u():p(h[x],h[x+1],h[x+2]);return $}},this.resolve=function(r,t,e,n){return this.study(r)(t,e,n)}}function s(r,t,e){this.fromConfig=function(r,t,e){return g(r.template)?this.fromString(r.template,t):g(r.templateUrl)?this.fromUrl(r.templateUrl,t):g(r.templateProvider)?this.fromProvider(r.templateProvider,t,e):null},this.fromString=function(r,t){return w(r)?r(t):r},this.fromUrl=function(e,n){return w(e)&&(e=e(n)),null==e?null:r.get(e,{cache:t}).then(function(r){return r.data})},this.fromProvider=function(r,t,n){return e.invoke(r,null,n||{params:t})}}function l(r){function t(t){if(!/^\w+(-+\w+)*$/.test(t))throw Error("Invalid parameter name '"+t+"' in pattern '"+r+"'");if(o[t])throw Error("Duplicate parameter name '"+t+"' in pattern '"+r+"'");o[t]=!0,l.push(t)}function e(r){return r.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var n,a=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,o={},i="^",u=0,s=this.segments=[],l=this.params=[];this.source=r;for(var c,f,h;(n=a.exec(r))&&(c=n[2]||n[3],f=n[4]||("*"==n[1]?".*":"[^/]*"),h=r.substring(u,n.index),!(h.indexOf("?")>=0));)i+=e(h)+"("+f+")",t(c),s.push(h),u=a.lastIndex;h=r.substring(u);var p=h.indexOf("?");if(p>=0){var v=this.sourceSearch=h.substring(p);h=h.substring(0,p),this.sourcePath=r.substring(0,u+p),y(v.substring(1).split(/[&?]/),t)}else this.sourcePath=r,this.sourceSearch="";i+=e(h)+"$",s.push(h),this.regexp=RegExp(i),this.prefix=s[0]}function c(){this.compile=function(r){return new l(r)},this.isMatcher=function(r){return E(r)&&w(r.exec)&&w(r.format)&&w(r.concat)},this.$get=function(){return this}}function f(r){function t(r){var t=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(r.source);return null!=t?t[1].replace(/\\(.)/g,"$1"):""}function e(r,t){return r.replace(/\$(\$|\d{1,2})/,function(r,e){return t["$"===e?0:Number(e)]})}function n(r,t,e){if(!e)return!1;var n=r.invoke(t,t,{$match:e});return g(n)?n:!0}var a=[],o=null;this.rule=function(r){if(!w(r))throw Error("'rule' must be a function");return a.push(r),this},this.otherwise=function(r){if(b(r)){var t=r;r=function(){return t}}else if(!w(r))throw Error("'rule' must be a function");return o=r,this},this.when=function(a,o){var i,u=b(o);if(b(a)&&(a=r.compile(a)),!u&&!w(o)&&!x(o))throw Error("invalid 'handler' in when()");var s={matcher:function(t,e){return u&&(i=r.compile(e),e=["$match",function(r){return i.format(r)}]),P(function(r,a){return n(r,e,t.exec(a.path(),a.search()))},{prefix:b(t.prefix)?t.prefix:""})},regex:function(r,a){if(r.global||r.sticky)throw Error("when() RegExp must not be global or sticky");return u&&(i=a,a=["$match",function(r){return e(i,r)}]),P(function(t,e){return n(t,a,r.exec(e.path()))},{prefix:t(r)})}},l={matcher:r.isMatcher(a),regex:a instanceof RegExp};for(var c in l)if(l[c])return this.rule(s[c](a,o));throw Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(r,t,e){function n(){function t(t){var n=t(e,r);return n?(b(n)&&r.replace().url(n),!0):!1}var n,i=a.length;for(n=0;i>n;n++)if(t(a[n]))return;o&&t(o)}return t.$on("$locationChangeSuccess",n),{}}]}function h(r,a,o){function u(r,t){var n=b(r),a=n?r:r.name,o=0===a.indexOf(".")||0===a.indexOf("^");if(o){if(!t)throw Error("No reference point given for path '"+a+"'");for(var i=a.split("."),u=0,s=i.length,l=t;s>u;u++)if(""!==i[u]||0!==u){if("^"!==i[u])break;if(!l.parent)throw Error("Path '"+a+"' not valid for state '"+t.name+"'");l=l.parent}else l=t;i=i.slice(u).join("."),a=l.name+(l.name&&i?".":"")+i}var c=m[a];return!c||!n&&(n||c!==r&&c.self!==r)?e:c}function s(t){t=n(t,{self:t,resolve:t.resolve||{},toString:function(){return this.name}});var e=t.name;if(!b(e)||e.indexOf("@")>=0)throw Error("State must have a valid name");if(m[e])throw Error("State '"+e+"'' is already defined");for(var a in d)t[a]=d[a](t);return m[e]=t,!t["abstract"]&&t.url&&r.when(t.url,["$match","$stateParams",function(r,e){$.$current.navigable==t&&h(r,e)||$.transitionTo(t,r,!1)}]),t}function l(r,t){return E(r)?t=r:t.name=r,s(t),this}function c(r,t,a,s,l,c,m){function d(r,e,n,o,i){var u=n?e:p(r.params,e),s={$stateParams:u};i.resolve=l.resolve(r.resolve,s,i.resolve,r);var c=[i.resolve.then(function(r){i.globals=r})];return o&&c.push(o),y(r.views,function(t,e){var n=t.resolve&&t.resolve!==r.resolve?t.resolve:{};n.$template=[function(){return a.load(e,{view:t,locals:s,params:u,notify:!1})||""}],c.push(l.resolve(n,s,i.resolve,r).then(function(n){n.$$controller=t.controller,n.$$state=r,i[e]=n}))}),t.all(c).then(function(){return i})}var w=t.reject(Error("transition superseded")),b=t.reject(Error("transition prevented"));return v.locals={resolve:null,globals:{$stateParams:{}}},$={params:{},current:v.self,$current:v,transition:null},$.go=function(r,t,e){return this.transitionTo(r,t,P({inherit:!0,relative:$.$current},e))},$.transitionTo=function(e,a,o){g(o)||(o=o===!0||o===!1?{location:o}:{}),a=a||{},o=P({location:!0,inherit:!1,relative:null},o);var l=u(e,o.relative);if(!g(l))throw Error("No such state "+l);if(l["abstract"])throw Error("Cannot transition to abstract state '"+e+"'");o.inherit&&(a=i(c,a||{},$.$current,l)),e=l;var p,E,x=e.path,y=$.$current,C=$.params,S=y.path,O=v.locals,k=[];for(p=0,E=x[p];E&&E===S[p]&&h(a,C,E.ownParams);p++,E=x[p])O=k[p]=E.locals;if(e===y&&O===y.locals)return $.transition=null,t.when($.current);a=f(e.params,a||{});var R=r.$broadcast("$stateChangeStart",e.self,a,y.self,C);if(R.defaultPrevented)return b;for(var I=t.when(O),M=p;x.length>M;M++,E=x[M])O=k[M]=n(O),I=d(E,a,E===e,I,O);var U=$.transition=I.then(function(){var t,n,i;if($.transition!==U)return w;for(t=S.length-1;t>=p;t--)i=S[t],i.self.onExit&&s.invoke(i.self.onExit,i.self,i.locals.globals),i.locals=null;for(t=p;x.length>t;t++)n=x[t],n.locals=k[t],n.self.onEnter&&s.invoke(n.self.onEnter,n.self,n.locals.globals);$.$current=e,$.current=e.self,$.params=a,j($.params,c),$.transition=null;var u=e.navigable;return o.location&&u&&m.url(u.url.format(u.locals.globals.$stateParams)),r.$broadcast("$stateChangeSuccess",e.self,a,y.self,C),$.current},function(n){return $.transition!==U?w:($.transition=null,r.$broadcast("$stateChangeError",e.self,a,y.self,C,n),t.reject(n))});return U},$.is=function(r){var t=u(r);return g(t)?$.$current===t:e},$.includes=function(r){var t=u(r);return g(t)?g($.$current.includes[t.name]):e},$.href=function(r,t,e){e=P({lossy:!0,inherit:!1,relative:$.$current},e||{});var n=u(r,e.relative);if(!g(n))return null;t=i(c,t||{},$.$current,n);var a=n&&e.lossy?n.navigable:n,s=a&&a.url?a.url.format(f(n.params,t||{})):null;return!o.html5Mode()&&s?"#"+s:s},$.get=function(r){var t=u(r);return t&&t.self?t.self:null},$}function f(r,t){var e={};return y(r,function(r){var n=t[r];e[r]=null!=n?n+"":null}),e}function h(r,t,e){if(!e){e=[];for(var n in r)e.push(n)}for(var a=0;e.length>a;a++){var o=e[a];if(r[o]!=t[o])return!1}return!0}function p(r,t){var e={};return y(r,function(r){e[r]=t[r]}),e}var v,$,m={},d={parent:function(r){if(g(r.parent)&&r.parent)return u(r.parent);var t=/^(.+)\.[^.]+$/.exec(r.name);return t?u(t[1]):v},data:function(r){return r.parent&&r.parent.data&&(r.data=r.self.data=t.extend({},r.parent.data,r.data)),r.data},url:function(r){var t=r.url;if(b(t))return"^"==t.charAt(0)?a.compile(t.substring(1)):(r.parent.navigable||v).url.concat(t);if(a.isMatcher(t)||null==t)return t;throw Error("Invalid url '"+t+"' in state '"+r+"'")},navigable:function(r){return r.url?r:r.parent?r.parent.navigable:null},params:function(r){if(!r.params)return r.url?r.url.parameters():r.parent.params;if(!x(r.params))throw Error("Invalid params in state '"+r+"'");if(r.url)throw Error("Both params and url specicified in state '"+r+"'");return r.params},views:function(r){var t={};return y(g(r.views)?r.views:{"":r},function(e,n){0>n.indexOf("@")&&(n+="@"+r.parent.name),t[n]=e}),t},ownParams:function(r){if(!r.parent)return r.params;var t={};y(r.params,function(r){t[r]=!0}),y(r.parent.params,function(e){if(!t[e])throw Error("Missing required parameter '"+e+"' in state '"+r.name+"'");t[e]=!1});var e=[];return y(t,function(r,t){r&&e.push(t)}),e},path:function(r){return r.parent?r.parent.path.concat(r):[]},includes:function(r){var t=r.parent?P({},r.parent.includes):{};return t[r.name]=!0,t}};v=s({name:"",url:"^",views:null,"abstract":!0}),v.navigable=null,this.state=l,this.$get=c,c.$inject=["$rootScope","$q","$view","$injector","$resolve","$stateParams","$location","$urlRouter"]}function p(){function r(r,t){return{load:function(e,n){var a,o={template:null,controller:null,view:null,locals:null,notify:!0,async:!0,params:{}};return n=P(o,n),n.view&&(a=t.fromConfig(n.view,n.params,n.locals)),a&&n.notify&&r.$broadcast("$viewContentLoading",n),a}}}this.$get=r,r.$inject=["$rootScope","$templateFactory"]}function v(r,e,n,a,o){var i;try{i=a.get("$animator")}catch(u){}var s=!1,l={restrict:"ECA",terminal:!0,transclude:!0,compile:function(a,u,c){return function(a,u,f){function h(t){var i=r.$current&&r.$current.locals[$];if(i!==v){var s=w(d&&t);if(s.remove(u),p&&(p.$destroy(),p=null),!i)return v=null,E.state=null,s.restore(c(a),u);v=i,E.state=i.$$state;var l=e(s.populate(i.$template,u));if(p=a.$new(),i.$$controller){i.$scope=p;var f=n(i.$$controller,i);u.children().data("$ngControllerController",f)}l(p),p.$emit("$viewContentLoaded"),m&&p.$eval(m),o()}}var p,v,$=f[l.name]||f.name||"",m=f.onload||"",d=g(i)&&i(a,f),w=function(r){return{"true":{remove:function(r){d.leave(r.contents(),r)},restore:function(r,t){d.enter(r,t)},populate:function(r,e){var n=t.element("").html(r).contents();return d.enter(n,e),n}},"false":{remove:function(r){r.html("")},restore:function(r,t){t.append(r)},populate:function(r,t){return t.html(r),t.contents()}}}[""+r]};u.append(c(a));var b=u.parent().inheritedData("$uiView");0>$.indexOf("@")&&($=$+"@"+(b?b.state.name:""));var E={name:$,state:null};u.data("$uiView",E);var x=function(){if(!s){s=!0;try{h(!0)}catch(r){throw s=!1,r}s=!1}};a.$on("$stateChangeSuccess",x),a.$on("$viewContentLoading",x),h(!1)}}};return l}function $(r){var t=r.match(/^([^(]+?)\s*(\((.*)\))?$/);if(!t||4!==t.length)throw Error("Invalid state ref '"+r+"'");return{state:t[1],paramExpr:t[3]||null}}function m(r){return{restrict:"A",link:function(t,n,a){var o=$(a.uiSref),i=null,u=r.$current,s="FORM"===n[0].nodeName,l=s?"action":"href",c=!0,f=n.parent().inheritedData("$uiView");f&&f.state&&f.state.name&&(u=f.state);var h=function(t){if(t&&(i=t),c){var a=r.href(o.state,i,{relative:u});return a?(n[0][l]=a,e):(c=!1,!1)}};o.paramExpr&&(t.$watch(o.paramExpr,function(r,t){r!==t&&h(r)},!0),i=t.$eval(o.paramExpr)),h(),s||n.bind("click",function(e){1!=e.which||e.ctrlKey||e.metaKey||e.shiftKey||(r.go(o.state,i,{relative:u}),t.$apply(),e.preventDefault())})}}}function d(r,t){function a(r){this.locals=r.locals.globals,this.params=this.locals.$stateParams}function o(){this.locals=null,this.params=null}function i(e,i){if(null!=i.redirectTo){var u,l=i.redirectTo;if(b(l))u=l;else{if(!w(l))throw Error("Invalid 'redirectTo' in when()");u=function(r,t){return l(r,t.path(),t.search())}}t.when(e,u)}else r.state(n(i,{parent:null,name:"route:"+encodeURIComponent(e),url:e,onEnter:a,onExit:o}));return s.push(i),this}function u(r,t,n){function a(r){return""!==r.name?r:e}var o={routes:s,params:n,current:e};return t.$on("$stateChangeStart",function(r,e,n,o){t.$broadcast("$routeChangeStart",a(e),a(o))}),t.$on("$stateChangeSuccess",function(r,e,n,i){o.current=a(e),t.$broadcast("$routeChangeSuccess",a(e),a(i)),j(n,o.params)}),t.$on("$stateChangeError",function(r,e,n,o,i,u){t.$broadcast("$routeChangeError",a(e),a(o),u)}),o}var s=[];a.$inject=["$$state"],this.when=i,this.$get=u,u.$inject=["$state","$rootScope","$routeParams"]}var g=t.isDefined,w=t.isFunction,b=t.isString,E=t.isObject,x=t.isArray,y=t.forEach,P=t.extend,j=t.copy;t.module("ui.router.util",["ng"]),t.module("ui.router.router",["ui.router.util"]),t.module("ui.router.state",["ui.router.router","ui.router.util"]),t.module("ui.router",["ui.router.state"]),t.module("ui.router.compat",["ui.router"]),u.$inject=["$q","$injector"],t.module("ui.router.util").service("$resolve",u),s.$inject=["$http","$templateCache","$injector"],t.module("ui.router.util").service("$templateFactory",s),l.prototype.concat=function(r){return new l(this.sourcePath+r+this.sourceSearch)},l.prototype.toString=function(){return this.source},l.prototype.exec=function(r,t){var e=this.regexp.exec(r);if(!e)return null;var n,a=this.params,o=a.length,i=this.segments.length-1,u={};if(i!==e.length-1)throw Error("Unbalanced capture group in route '"+this.source+"'");for(n=0;i>n;n++)u[a[n]]=e[n+1];for(;o>n;n++)u[a[n]]=t[a[n]];return u},l.prototype.parameters=function(){return this.params},l.prototype.format=function(r){var t=this.segments,e=this.params;if(!r)return t.join("");var n,a,o,i=t.length-1,u=e.length,s=t[0];for(n=0;i>n;n++)o=r[e[n]],null!=o&&(s+=encodeURIComponent(o)),s+=t[n+1];for(;u>n;n++)o=r[e[n]],null!=o&&(s+=(a?"&":"?")+e[n]+"="+encodeURIComponent(o),a=!0);return s},t.module("ui.router.util").provider("$urlMatcherFactory",c),f.$inject=["$urlMatcherFactoryProvider"],t.module("ui.router.router").provider("$urlRouter",f),h.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],t.module("ui.router.state").value("$stateParams",{}).provider("$state",h),p.$inject=[],t.module("ui.router.state").provider("$view",p),v.$inject=["$state","$compile","$controller","$injector","$anchorScroll"],t.module("ui.router.state").directive("uiView",v),m.$inject=["$state"],t.module("ui.router.state").directive("uiSref",m),d.$inject=["$stateProvider","$urlRouterProvider"],t.module("ui.router.compat").provider("$route",d).directive("ngView",v)})(window,window.angular);
\ No newline at end of file
+module&&exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return y(new(y(function(){},{prototype:a})),b)}function e(a){return x(arguments,function(b){b!==a&&x(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path)if(""!==a.path[d]){if(!b.path[d])break;c.push(a.path[d])}return c}function g(a,b,c,d){var e,g=f(c,d),h={},i=[];for(var j in g)if(g[j].params&&g[j].params.length){e=g[j].params;for(var k in e)i.indexOf(e[k])>=0||(i.push(e[k]),h[e[k]]=a[e[k]])}return y({},h,b)}function h(a,b){var d=1,f=2,g={},h=[],i=g,j=y(a.when(g),{$$promises:g,$$values:g});this.study=function(g){function k(a,c){if(o[c]!==f){if(n.push(c),o[c]===d)throw n.splice(0,n.indexOf(c)),new Error("Cyclic dependency: "+n.join(" -> "));if(o[c]=d,u(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);x(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return v(a)&&a.then&&a.$$promises}if(!v(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return x(g,k),g=n=o=null,function(d,f,g){function h(){--t||(u||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!s(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;x(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!v(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=y({},d),t=1+m.length/3,u=!1;if(s(f.$$failure))return k(f.$$failure),p;f.$$values?(u=e(r,f.$$values),h()):(y(q,f.$$promises),f.then(h,k));for(var w=0,z=m.length;z>w;w+=3)d.hasOwnProperty(m[w])?h():n(m[w],m[w+1],m[w+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function i(a,b,c){this.fromConfig=function(a,b,c){return s(a.template)?this.fromString(a.template,b):s(a.templateUrl)?this.fromUrl(a.templateUrl,b):s(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return t(a)?a(b):a},this.fromUrl=function(c,d){return t(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function j(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.typeMap={},this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),s(this.types[l])&&(this.typeMap[k]=l,l=this.types[l].pattern),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),x(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function k(){this.compile=function(a){return new j(a)},this.isMatcher=function(a){return v(a)&&t(a.exec)&&t(a.format)&&t(a.concat)},this.type=function(a,b){return j.prototype.type(a,b)},this.$get=function(){return this}}function l(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return s(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!t(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(u(a)){var b=a;a=function(){return b}}else if(!t(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=u(f);if(u(e)&&(e=a.compile(e)),!h&&!t(f)&&!w(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),y(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:u(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),y(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(){function b(b){var d=b(c,a);return d?(u(d)&&a.replace().url(d),!0):!1}var d,g=e.length;for(d=0;g>d;d++)if(b(e[d]))return;f&&b(f)}return b.$on("$locationChangeSuccess",d),{}}]}function m(a,e,f){function h(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function i(a,b){var d=u(a),e=d?a:a.name,f=h(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");for(var g=e.split("."),i=0,j=g.length,k=b;j>i;i++)if(""!==g[i]||0!==i){if("^"!==g[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;g=g.slice(i).join("."),e=k.name+(k.name&&g?".":"")+g}var l=A[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function j(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!u(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(A[c])throw new Error("State '"+c+"'' is already defined");for(var e in B)t(B[e])&&(b[e]=B[e](b,B.$delegates[e]));return A[c]=b,!b["abstract"]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){r.$current.navigable==b&&o(b,a,c)||r.transitionTo(b,a,!1)}]),b}function k(a,b){return u(a)&&!s(b)?B[a]:t(b)&&u(a)?(B[a]&&!B.$delegates[a]&&(B.$delegates[a]=B[a]),B[a]=b,this):this}function l(a,b){return v(a)?b=a:b.name=a,j(b),this}function m(a,e,h,j,k,l,m){function t(a,b,c,d,f){var g=c?b:p(a.params,b),i={$stateParams:g};f.resolve=k.resolve(a.resolve,i,f.resolve,a);var j=[f.resolve.then(function(a){f.globals=a})];return d&&j.push(d),x(a.views,function(b,c){var d=b.resolve&&b.resolve!==a.resolve?b.resolve:{};d.$template=[function(){return h.load(c,{view:b,locals:i,params:g,notify:!1})||""}],j.push(k.resolve(d,i,f.resolve,a).then(function(d){d.$$controller=b.controller,d.$$state=a,f[c]=d}))}),e.all(j).then(function(){return f})}var u=e.reject(new Error("transition superseded")),v=e.reject(new Error("transition prevented")),w=e.reject(new Error("transition aborted")),B=e.reject(new Error("transition failed"));return q.locals={resolve:null,globals:{$stateParams:{}}},r={params:{},current:q.self,$current:q,transition:null},r.go=function(a,b,c){return this.transitionTo(a,b,y({inherit:!0,relative:r.$current},c))},r.transitionTo=function(b,c,f){s(f)||(f=f===!0||f===!1?{location:f}:{}),c=c||{},f=y({location:!0,inherit:!1,relative:null,broadcastStateChangeSuccess:!0,$retry:!1},f);var h,k=r.$current,p=r.params,x=k.path,A=i(b,f.relative);if(!s(A)){var C={to:b,toParams:c,options:f};if(h=a.$broadcast("$stateNotFound",C,k.self,p),h.defaultPrevented)return w;if(h.retry){if(f.$retry)return B;var D=r.transition=e.when(h.retry);return D.then(function(){return D!==r.transition?u:(C.options.$retry=!0,r.transitionTo(C.to,C.toParams,C.options))},function(){return w}),D}if(b=C.to,c=C.toParams,f=C.options,A=i(b,f.relative),!s(A)){if(f.relative)throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'");throw new Error("No such state '"+b+"'")}}if(A["abstract"])throw new Error("Cannot transition to abstract state '"+b+"'");f.inherit&&(c=g(l,c||{},r.$current,A)),b=A;var E,F,G=b.path,H=q.locals,I=[];for(E=0,F=G[E];F&&F===x[E]&&o(F,c,p,F.ownParams);E++,F=G[E])H=I[E]=F.locals;if(b===k&&H===k.locals)return r.transition=null,e.when(r.current);if(c=n(b.params,c||{}),h=a.$broadcast("$stateChangeStart",b.self,c,k.self,p),h.defaultPrevented)return v;for(var J=e.when(H),K=E;K=E;d--)g=x[d],g.self.onExit&&j.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=E;d").html(a).contents();return r.enter(d,c),d}},"false":{remove:function(a){a.html("")},restore:function(a,b){b.append(a)},populate:function(a,b){return b.html(a),b.contents()}}}[a.toString()]};h.append(k(e));var u=h.parent().inheritedData("$uiView");p.indexOf("@")<0&&(p=p+"@"+(u?u.state.name:""));var v={name:p,state:null};h.data("$uiView",v);var w=function(){if(!i){i=!0;try{m(!0)}catch(a){throw i=!1,a}i=!1}};e.$on("$stateChangeSuccess",w),e.$on("$viewContentLoading",w),m(!1)}}};return j}function p(a){var b=a.match(/^([^(]+?)\s*(\((.*)\))?$/);if(!b||4!==b.length)throw new Error("Invalid state ref '"+a+"'");return{state:b[1],paramExpr:b[3]||null}}function q(a){return{restrict:"A",link:function(b,c,d){var e=p(d.uiSref),f=null,g=a.$current,h="FORM"===c[0].nodeName,i=h?"action":"href",j=!0,k=c.parent().inheritedData("$uiView");k&&k.state&&k.state.name&&(g=k.state);var l=function(b){if(b&&(f=b),j){var d=a.href(e.state,f,{relative:g});return d?(c[0][i]=d,void 0):(j=!1,!1)}};e.paramExpr&&(b.$watch(e.paramExpr,function(a){a!==f&&l(a)},!0),f=b.$eval(e.paramExpr)),l(),h||c.bind("click",function(c){var d=c.which||c.button;1!=d||c.ctrlKey||c.metaKey||c.shiftKey||(a.go(e.state,f,{relative:g}),b.$apply(),c.preventDefault())})}}}function r(a,b){function e(a){this.locals=a.locals.globals,this.params=this.locals.$stateParams}function f(){this.locals=null,this.params=null}function g(c,g){if(null!=g.redirectTo){var h,j=g.redirectTo;if(u(j))h=j;else{if(!t(j))throw new Error("Invalid 'redirectTo' in when()");h=function(a,b){return j(a,b.path(),b.search())}}b.when(c,h)}else a.state(d(g,{parent:null,name:"route:"+encodeURIComponent(c),url:c,onEnter:e,onExit:f}));return i.push(g),this}function h(a,b,d){function e(a){return""!==a.name?a:c}var f={routes:i,params:d,current:c};return b.$on("$stateChangeStart",function(a,c,d,f){b.$broadcast("$routeChangeStart",e(c),e(f))}),b.$on("$stateChangeSuccess",function(a,c,d,g){f.current=e(c),b.$broadcast("$routeChangeSuccess",e(c),e(g)),z(d,f.params)}),b.$on("$stateChangeError",function(a,c,d,f,g,h){b.$broadcast("$routeChangeError",e(c),e(f),h)}),f}var i=[];e.$inject=["$$state"],this.when=g,this.$get=h,h.$inject=["$state","$rootScope","$routeParams"]}var s=b.isDefined,t=b.isFunction,u=b.isString,v=b.isObject,w=b.isArray,x=b.forEach,y=b.extend,z=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),h.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",h),i.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",i),j.prototype.concat=function(a){return new j(this.sourcePath+a+this.sourceSearch)},j.prototype.toString=function(){return this.source},j.prototype.exec=function(a,b){var c=this.regexp.exec(a),d=this.types,e=this.typeMap;if(!c)return null;var f,g=this.params,h=g.length,i=this.segments.length-1,j={};if(i!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(f=0;i>f;f++)j[g[f]]=c[f+1];for(;h>f;f++)j[g[f]]=b[g[f]];var k={};return x(j,function(a,b){k[b]=s(e[b])?d[e[b]].decode(a):a}),k},j.prototype.parameters=function(){return this.params},j.prototype.format=function(a){var b=this.segments,c=this.params,d=this.types,e=this.typeMap;if(!a)return b.join("");var f,g,h,i=b.length-1,j=c.length,k=b[0],l={};for(x(a,function(a,b){if(s(e[b])){var c=d[e[b]];c.is(a)&&(l[b]=c.encode(a))}else l[b]=a}),f=0;i>f;f++)h=l[c[f]],null!=h&&(k+=encodeURIComponent(h)),k+=b[f+1];for(;j>f;f++)h=l[c[f]],null!=h&&(k+=(g?"&":"?")+c[f]+"="+encodeURIComponent(h),g=!0);return k},j.prototype.types={"boolean":{pattern:"true|false",is:function(a){return a===!0||a===!1},equals:function(a,b){return this.is(a)&&this.is(b)?a===b:!1},encode:function(a){return a.toString().toLowerCase()},decode:function(a){return a&&"true"===a.toLowerCase()?!0:a&&"false"===a.toLowerCase()?!1:c}},integer:{pattern:"[0-9]+",is:function(a){return"number"==typeof a&&0===a%1},equals:function(a,b){return this.is(a)&&this.is(b)?a===b:!1},encode:function(a){return a.toString()},decode:function(a){return parseInt(a,10)}}},j.prototype.type=function(a,b){if(!b&&j.prototype.types[a])return j.prototype.types[a];if(!(u(a)&&v(b)&&t(b.decode)&&t(b.encode)))throw new Error("Invalid type '"+a+"'");u(b.pattern)||(b.pattern=".*"),t(b.is)||(b.is=function(a){return JSON.stringify(b.decode(b.encode(a)))===JSON.stringify(a)}),t(b.equals)||(b.equals=function(a,c){return b.is(a)&&b.is(c)?b.encode(a)===b.encode(c):!1}),j.prototype.types[a]=b},b.module("ui.router.util").provider("$urlMatcherFactory",k),l.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",l),m.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",m),n.$inject=[],b.module("ui.router.state").provider("$view",n),o.$inject=["$state","$compile","$controller","$injector","$anchorScroll"],b.module("ui.router.state").directive("uiView",o),q.$inject=["$state"],b.module("ui.router.state").directive("uiSref",q),r.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",r).directive("ngView",o)}(window,window.angular);
\ No newline at end of file
diff --git a/release/doc/$resolve.html b/release/doc/$resolve.html
index 224845b81..b224c56ac 100644
--- a/release/doc/$resolve.html
+++ b/release/doc/$resolve.html
@@ -118,40 +118,32 @@ resolve$q promise. If a promise is returned it will be resolved and the resulting value will be
used instead. Dependencies of invocables are resolved (in this order of precedence)
-
- from the specified
locals
- from another invocable that is part of this
$resolve
call
- from an invocable that is inherited from a
parent
call to $resolve
(or recursively
from any ancestor $resolve
of that parent).
-
The return value of $resolve
is a promise for an object that contains (in this order of precedence)
-
- any
locals
(if specified)
- the resolved return values of all injectables
- any values inherited from a
parent
call to $resolve
(if specified)
-
The promise will resolve after the parent
promise (if any) and all promises returned by injectables
have been resolved. If any invocable (or $injector.invoke
) throws an exception, or if a promise
returned by an invocable is rejected, the $resolve
promise is immediately rejected with the same error.
A rejection of a parent
promise (if specified) will likewise be propagated immediately. Once the
$resolve
promise has been rejected, no further invocables will be called.
-
Cyclic dependencies between invocables are not permitted and will caues $resolve
to throw an
error. As a special case, an injectable can depend on a parameter with the same name as the injectable,
which will be fulfilled from the parent
injectable of the same name. This allows inherited values
to be decorated. Note that in this case any other injectable in the same $resolve
with the same
dependency would see the decorated value, not the inherited value.
-
Note that missing dependencies -- unlike cyclic dependencies -- will cause an (asynchronous) rejection
of the $resolve
promise rather than a (synchronous) exception.
-
Invocables are invoked eagerly as soon as all dependencies are available. This is true even for
dependencies inherited from a parent
call to $resolve
.
-
As a special case, an invocable can be a string, in which case it is taken to be a service name
to be passed to $injector.get()
. This is supported primarily for backwards-compatibility with the
resolve
property of $routeProvider
routes.
@@ -196,10 +188,7 @@ Parameters:
-Object.<string, Function
-|
-
-string>
+Object.<string, (Function|string)>
@@ -210,6 +199,8 @@ Parameters:
+
+
|
@@ -241,6 +232,8 @@ Parameters:
+
+
@@ -272,6 +265,8 @@ Parameters:
+
+
@@ -303,6 +298,8 @@ Parameters:
+
+
@@ -352,6 +349,10 @@ Parameters:
+
+
+
+
@@ -493,6 +494,10 @@ Parameters:
+
+
+
+
@@ -541,9 +546,10 @@ Modules