diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..1242b88
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,24 @@
+# 1.3.0 (2014-05-15)
+
+## Features
+
+- New function `$routeSegment.getSegmentUrl(segmentName, routeParams)` which can return URL for the given segment ([2b255](https://github.com/artch/angular-route-segment/commit/2b255db63b7273be9f0c75b19c464620835db9b9)).
+- Some handy filters like `routeSegmentUrl`,`routeSegmentEqualsTo`, etc ([2b255](https://github.com/artch/angular-route-segment/commit/2b255db63b7273be9f0c75b19c464620835db9b9)).
+- New segment config option `default:true` which can be set if this child segment should be loaded by default when no child segment is specified in the route ([2eee0](https://github.com/artch/angular-route-segment/commit/2eee0a1dc7d6a6ff031d8451c06d4da5ae7e50fc)).
+- `template` and `templateUrl` can be set as injectable functions ([8d1ac](https://github.com/artch/angular-route-segment/commit/8d1ac0d1ea1aee9243f90e32e4e4387a717049ac)).
+
+See updated [docs](https://github.com/artch/angular-route-segment/blob/master/README.md) and [demo example](http://angular-route-segment.com/src/example/) for usage.
+
+## Bug fixes
+
+- Fixed a bug when reloading a segment does not recover after the resolving error has gone ([ed11d](https://github.com/artch/angular-route-segment/commit/ed11d58e495ea7c611a59373fd6b909de1be33e3)).
+
+
+# 1.2.4 (2014-05-08)
+
+- Fixed a bug with null exception on `contains` function ([1b014](https://github.com/artch/angular-route-segment/commit/1b014d3b5ea7740815c7e0b98467bdff556e6a5b) thanks to [paivaric](https://github.com/paivaric)).
+
+# 1.2.3 (2014-04-07)
+
+- Fixed a bug with double updates of view segments ([eb0d8](https://github.com/artch/angular-route-segment/commit/eb0d8a0c4aa01c2d8ab600aacef69e4a5479afd6)).
+- `options.autoLoadTemplates` is true by default ([afab3](https://github.com/artch/angular-route-segment/commit/afab3ae7b827b7141ebcf0b8130311dc5aac0d7d)).
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 9201e65..87e472e 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "angular-route-segment",
- "version": "1.2.4",
+ "version": "1.3.0",
"main": "build/angular-route-segment.js",
"ignore": [
"**/.*",
diff --git a/build/angular-route-segment.js b/build/angular-route-segment.js
index 2f7cae4..785932d 100644
--- a/build/angular-route-segment.js
+++ b/build/angular-route-segment.js
@@ -1,5 +1,5 @@
/**
- * angular-route-segment 1.2.3
+ * angular-route-segment 1.3.0
* https://angular-route-segment.com
* @author Artem Chivchalov
* @license MIT License http://opensource.org/licenses/MIT
@@ -8,7 +8,8 @@
(function(angular) {
-angular.module( 'route-segment', [] ).provider( '$routeSegment',
+var mod = angular.module( 'route-segment', [] );
+mod.provider( '$routeSegment',
['$routeProvider', function($routeProvider) {
var $routeSegmentProvider = this;
@@ -32,7 +33,8 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
};
var segments = this.segments = {},
- rootPointer = pointer(segments, null);
+ rootPointer = pointer(segments, null),
+ segmentRoutes = {};
function camelCase(name) {
return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
@@ -127,6 +129,7 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
*/
$routeSegmentProvider.when = function(route, name) {
$routeProvider.when(route, {segment: name});
+ segmentRoutes[name] = route;
return this;
};
@@ -186,6 +189,31 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
if(this.chain[i] && this.chain[i].name == val)
return true;
return false;
+ },
+
+ /**
+ * A method for reverse routing which can return the route URL for the specified segment name
+ * @param {string} segmentName The name of a segment as defined in `when()`
+ * @param {Object} routeParams Route params hash to be put into route URL template
+ */
+ getSegmentUrl: function(segmentName, routeParams) {
+ var url, i, m;
+ if(!segmentRoutes[segmentName])
+ throw new Error('Can not get URL for segment with name `'+segmentName+'`');
+
+ routeParams = angular.extend({}, $routeParams, routeParams || {});
+
+ url = segmentRoutes[segmentName];
+ for(i in routeParams) {
+ var regexp = new RegExp('\:'+i+'[\*\?]?','g');
+ url = url.replace(regexp, routeParams[i]);
+ }
+ url = url.replace(/\/\:.*?\?/g, '');
+
+ if(m = url.match(/\/\:([^\/]*)/))
+ throw new Error('Route param `'+m[1]+'` is not specified for route `'+segmentRoutes[segmentName]+'`');
+
+ return url;
}
};
@@ -199,7 +227,7 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
var segmentName = route.segment;
var segmentNameChain = segmentName.split(".");
- var updates = [];
+ var updates = [], lastUpdateIndex = -1;
for(var i=0; i < segmentNameChain.length; i++) {
@@ -211,8 +239,10 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
updates.length == 0 && !isDependenciesChanged(newSegment))
// if we went back to the same state as we were before resolving new segment
resolvingSemaphoreChain[i] = newSegment.name;
- else
+ else {
updates.push({index: i, newSegment: newSegment});
+ lastUpdateIndex = i;
+ }
}
}
@@ -239,17 +269,12 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
updateSegment(j, null);
}
}
-
-
}
})
})(i);
}
-
}
-
-
curSegmentPromise.then(function() {
// Removing redundant segment in case if new segment chain is shorter than old one
@@ -257,13 +282,41 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
var oldLength = $routeSegment.chain.length;
var shortenBy = $routeSegment.chain.length - segmentNameChain.length;
$routeSegment.chain.splice(-shortenBy, shortenBy);
- for(var i=segmentNameChain.length; i < oldLength; i++)
+ for(var i=segmentNameChain.length; i < oldLength; i++) {
updateSegment(i, null);
+ lastUpdateIndex = $routeSegment.chain.length-1;
+ }
+ }
+ }).then(function() {
+
+ var defaultChildUpdatePromise = $q.when();
+
+ if(lastUpdateIndex == $routeSegment.chain.length-1) {
+
+ var curSegment = getSegmentInChain(lastUpdateIndex, $routeSegment.name.split("."));
+
+ while(curSegment) {
+ var children = curSegment.children, index = lastUpdateIndex+1;
+ curSegment = null;
+ for (var i in children) {
+ (function(i, children, index) {
+ if (children[i].params.default) {
+ defaultChildUpdatePromise = defaultChildUpdatePromise.then(function () {
+ return updateSegment(index, {name: i, params: children[i].params})
+ .then(function (result) {
+ if (result.success) broadcast(result.success);
+ });
+ });
+ curSegment = children[i];
+ lastUpdateIndex = index;
+ }
+ })(i, children, index);
+ }
+ }
}
- })
-
-
+ return defaultChildUpdatePromise;
+ });
}
});
@@ -312,15 +365,25 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
locals[key] = angular.isString(value) ? $injector.get(value) : $injector.invoke(value);
});
- if(params.template)
+ if(params.template) {
+
locals.$template = params.template;
+ if(angular.isFunction(locals.$template))
+ locals.$template = $injector.invoke(locals.$template);
+ }
- if(options.autoLoadTemplates && params.templateUrl)
- locals.$template =
- $http.get(params.templateUrl, {cache: $templateCache})
- .then(function(response) {
+ if(options.autoLoadTemplates && params.templateUrl) {
+
+ locals.$template = params.templateUrl;
+ if(angular.isFunction(locals.$template))
+ locals.$template = $injector.invoke(locals.$template);
+
+ locals.$template =
+ $http.get(locals.$template, {cache: $templateCache})
+ .then(function (response) {
return response.data;
});
+ }
return $q.all(locals).then(
@@ -334,7 +397,8 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
params: params,
locals: resolvedLocals,
reload: function() {
- updateSegment(index, this).then(function(result) {
+ var originalSegment = getSegmentInChain(index, $routeSegment.name.split("."));
+ updateSegment(index, originalSegment).then(function(result) {
if(result.success != undefined)
broadcast(index);
})
@@ -417,13 +481,65 @@ angular.module( 'route-segment', [] ).provider( '$routeSegment',
return {
name: nextName,
- params: curSegment.params
+ params: curSegment.params,
+ children: curSegment.children
};
}
return $routeSegment;
}];
-}])
+}]);
+
+/**
+ * Usage:
+ *
+ *
+ */
+mod.filter('routeSegmentUrl', ['$routeSegment', function($routeSegment) {
+ return function(segmentName, params) {
+ return $routeSegment.getSegmentUrl(segmentName, params);
+ }
+}]);
+
+/**
+ * Usage:
+ *
+ */
+mod.filter('routeSegmentEqualsTo', ['$routeSegment', function($routeSegment) {
+ return function(value) {
+ return $routeSegment.name == value;
+ }
+}]);
+
+/**
+ * Usage:
+ *
+ */
+mod.filter('routeSegmentStartsWith', ['$routeSegment', function($routeSegment) {
+ return function(value) {
+ return $routeSegment.startsWith(value);
+ }
+}]);
+
+/**
+ * Usage:
+ *
+ */
+mod.filter('routeSegmentContains', ['$routeSegment', function($routeSegment) {
+ return function(value) {
+ return $routeSegment.contains(value);
+ }
+}]);
+
+/**
+ * Usage:
+ *
+ */
+mod.filter('routeSegmentParam', ['$routeSegment', function($routeSegment) {
+ return function(value) {
+ return $routeSegment.$routeParams[value];
+ }
+}]);
})(angular);;'use strict';
diff --git a/build/angular-route-segment.min.js b/build/angular-route-segment.min.js
index 38f1c50..0c7dc8b 100644
--- a/build/angular-route-segment.min.js
+++ b/build/angular-route-segment.min.js
@@ -1,7 +1,7 @@
/**
- * angular-route-segment 1.2.3
+ * angular-route-segment 1.3.0
* https://angular-route-segment.com
* @author Artem Chivchalov
* @license MIT License http://opensource.org/licenses/MIT
*/
-"use strict";!function(a){a.module("route-segment",[]).provider("$routeSegment",["$routeProvider",function(b){function c(a){return a.replace(/([\:\-\_]+(.))/g,function(a,b,c,d){return d?c.toUpperCase():c})}function d(a,b){if(!a)throw new Error("Invalid pointer segment");var e;return{segment:function(b,d){return a[c(b)]={params:d},e=b,this},within:function(b){var g;if(b=b||e,g=a[c(b)])void 0==g.children&&(g.children={});else{if(f.strictMode)throw new Error("Cannot get into unknown `"+b+"` segment");g=a[c(b)]={params:{},children:{}}}return d(g.children,this)},up:function(){return b},root:function(){return h}}}var e=this,f=e.options={autoLoadTemplates:!0,strictMode:!1},g=this.segments={},h=d(g,null);e.when=function(a,c){return b.when(a,{segment:c}),this},a.extend(e,h),this.$get=["$rootScope","$q","$http","$templateCache","$route","$routeParams","$injector",function(b,d,e,h,i,j,k){function l(b){var c=!1;return b.params.dependencies&&a.forEach(b.params.dependencies,function(b){a.equals(q.$routeParams[b],j[b])||(c=!0)}),c}function m(a,b){return q.chain[a]&&q.chain[a].clearWatcher&&q.chain[a].clearWatcher(),b?(r[a]=b.name,b.params.untilResolved?n(a,b.name,b.params.untilResolved).then(function(c){return void 0!=c.success&&o(a),n(a,b.name,b.params)}):n(a,b.name,b.params)):(r[a]=null,o(a),void 0)}function n(c,g,i){var j=a.extend({},i.resolve);return a.forEach(j,function(b,c){j[c]=a.isString(b)?k.get(b):k.invoke(b)}),i.template&&(j.$template=i.template),f.autoLoadTemplates&&i.templateUrl&&(j.$template=e.get(i.templateUrl,{cache:h}).then(function(a){return a.data})),d.all(j).then(function(e){if(r[c]!=g)return d.reject();if(q.chain[c]={name:g,params:i,locals:e,reload:function(){m(c,this).then(function(a){void 0!=a.success&&o(c)})}},i.watcher){var f=function(){if(!a.isFunction(i.watcher))throw new Error("Watcher is not a function in segment `"+g+"`");return k.invoke(i.watcher,{},{segment:q.chain[c]})},h=f();q.chain[c].clearWatcher=b.$watch(f,function(a){a!=h&&(h=a,q.chain[c].reload())})}return{success:c}},function(b){if(i.resolveFailed){var e={error:function(){return d.when(b)}};return n(c,g,a.extend({resolve:e},i.resolveFailed))}throw new Error("Resolving failed with a reason `"+b+"`, but no `resolveFailed` "+"provided for segment `"+g+"`")})}function o(c){q.$routeParams=a.copy(j),q.name="";for(var d=0;d=b.length)return null;for(var d,e=g,f=0;a>=f;f++)d=b[f],void 0!=e[c(d)]&&(e=e[c(d)]),a>f&&(e=e.children);return{name:d,params:e.params}}var q={name:"",$routeParams:a.copy(j),chain:[],startsWith:function(a){var b=new RegExp("^"+a);return b.test(q.name)},contains:function(a){for(var b=0;b0||l(i))&&(q.chain[h]&&q.chain[h].name==i.name&&0==g.length&&!l(i)?r[h]=i.name:g.push({index:h,newSegment:i}))}var j=d.when();if(g.length>0)for(var h=0;hf.length){var a=q.chain.length,b=q.chain.length-f.length;q.chain.splice(-b,b);for(var c=f.length;a>c;c++)m(c,null)}})}}),q}]}])}(angular),function(a){a.module("view-segment",["route-segment"]).directive("appViewSegment",["$route","$compile","$controller","$routeParams","$routeSegment","$q","$injector","$timeout",function(b,c,d,e,f,g,h,i){return{restrict:"ECA",priority:500,compile:function(b,e){var g=b.html(),j=!0,k=a.element(document.createComment(" view-segment "));return b.prepend(k),function(l){function m(){p&&(r.leave(p),p=null),o&&(o.$destroy(),o=null)}function n(e){if(q=e,j&&(j=!1,b.replaceWith(k)),!e)return m(),p=b.clone(),p.html(g),r.enter(p,null,k),c(p,!1,499)(l),void 0;var f=a.extend({},e.locals),h=f&&f.$template;m(),p=b.clone(),p.html(h?h:g),r.enter(p,null,k);var i,n=c(p,!1,499);o=l.$new(),e.params.controller&&(f.$scope=o,i=d(e.params.controller,f),e.params.controllerAs&&(o[e.params.controllerAs]=i),p.data("$ngControllerController",i),p.children().data("$ngControllerController",i)),n(o),o.$emit("$viewContentLoaded"),o.$eval(t)}var o,p,q,r,s,t=e.onload||"",u=parseInt(e.appViewSegment);try{var v=h.get("$animator");r=v(l,e)}catch(w){}try{r=h.get("$animate")}catch(w){}f.chain[u]&&(s=i(function(){n(f.chain[u])},0)),l.$on("routeSegmentChange",function(a,b){s&&i.cancel(s),b.index==u&&q!=b.segment&&n(b.segment)})}}}}])}(angular);
\ No newline at end of file
+"use strict";!function(a){var b=a.module("route-segment",[]);b.provider("$routeSegment",["$routeProvider",function(b){function c(a){return a.replace(/([\:\-\_]+(.))/g,function(a,b,c,d){return d?c.toUpperCase():c})}function d(a,b){if(!a)throw new Error("Invalid pointer segment");var e;return{segment:function(b,d){return a[c(b)]={params:d},e=b,this},within:function(b){var g;if(b=b||e,g=a[c(b)])void 0==g.children&&(g.children={});else{if(f.strictMode)throw new Error("Cannot get into unknown `"+b+"` segment");g=a[c(b)]={params:{},children:{}}}return d(g.children,this)},up:function(){return b},root:function(){return h}}}var e=this,f=e.options={autoLoadTemplates:!0,strictMode:!1},g=this.segments={},h=d(g,null),i={};e.when=function(a,c){return b.when(a,{segment:c}),i[c]=a,this},a.extend(e,h),this.$get=["$rootScope","$q","$http","$templateCache","$route","$routeParams","$injector",function(b,d,e,h,j,k,l){function m(b){var c=!1;return b.params.dependencies&&a.forEach(b.params.dependencies,function(b){a.equals(r.$routeParams[b],k[b])||(c=!0)}),c}function n(a,b){return r.chain[a]&&r.chain[a].clearWatcher&&r.chain[a].clearWatcher(),b?(s[a]=b.name,b.params.untilResolved?o(a,b.name,b.params.untilResolved).then(function(c){return void 0!=c.success&&p(a),o(a,b.name,b.params)}):o(a,b.name,b.params)):(s[a]=null,void p(a))}function o(c,g,i){var j=a.extend({},i.resolve);return a.forEach(j,function(b,c){j[c]=a.isString(b)?l.get(b):l.invoke(b)}),i.template&&(j.$template=i.template,a.isFunction(j.$template)&&(j.$template=l.invoke(j.$template))),f.autoLoadTemplates&&i.templateUrl&&(j.$template=i.templateUrl,a.isFunction(j.$template)&&(j.$template=l.invoke(j.$template)),j.$template=e.get(j.$template,{cache:h}).then(function(a){return a.data})),d.all(j).then(function(e){if(s[c]!=g)return d.reject();if(r.chain[c]={name:g,params:i,locals:e,reload:function(){var a=q(c,r.name.split("."));n(c,a).then(function(a){void 0!=a.success&&p(c)})}},i.watcher){var f=function(){if(!a.isFunction(i.watcher))throw new Error("Watcher is not a function in segment `"+g+"`");return l.invoke(i.watcher,{},{segment:r.chain[c]})},h=f();r.chain[c].clearWatcher=b.$watch(f,function(a){a!=h&&(h=a,r.chain[c].reload())})}return{success:c}},function(b){if(i.resolveFailed){var e={error:function(){return d.when(b)}};return o(c,g,a.extend({resolve:e},i.resolveFailed))}throw new Error("Resolving failed with a reason `"+b+"`, but no `resolveFailed` provided for segment `"+g+"`")})}function p(c){r.$routeParams=a.copy(k),r.name="";for(var d=0;d=b.length)return null;for(var d,e=g,f=0;a>=f;f++)d=b[f],void 0!=e[c(d)]&&(e=e[c(d)]),a>f&&(e=e.children);return{name:d,params:e.params,children:e.children}}var r={name:"",$routeParams:a.copy(k),chain:[],startsWith:function(a){var b=new RegExp("^"+a);return b.test(r.name)},contains:function(a){for(var b=0;b0||m(j))&&(r.chain[i]&&r.chain[i].name==j.name&&0==g.length&&!m(j)?s[i]=j.name:(g.push({index:i,newSegment:j}),h=i))}var k=d.when();if(g.length>0)for(var i=0;if.length){var a=r.chain.length,b=r.chain.length-f.length;r.chain.splice(-b,b);for(var c=f.length;a>c;c++)n(c,null),h=r.chain.length-1}}).then(function(){var a=d.when();if(h==r.chain.length-1)for(var b=q(h,r.name.split("."));b;){var c=b.children,e=h+1;b=null;for(var f in c)!function(c,d,e){d[c].params.default&&(a=a.then(function(){return n(e,{name:c,params:d[c].params}).then(function(a){a.success&&p(a.success)})}),b=d[c],h=e)}(f,c,e)}return a})}}),r}]}]),b.filter("routeSegmentUrl",["$routeSegment",function(a){return function(b,c){return a.getSegmentUrl(b,c)}}]),b.filter("routeSegmentEqualsTo",["$routeSegment",function(a){return function(b){return a.name==b}}]),b.filter("routeSegmentStartsWith",["$routeSegment",function(a){return function(b){return a.startsWith(b)}}]),b.filter("routeSegmentContains",["$routeSegment",function(a){return function(b){return a.contains(b)}}]),b.filter("routeSegmentParam",["$routeSegment",function(a){return function(b){return a.$routeParams[b]}}])}(angular),function(a){a.module("view-segment",["route-segment"]).directive("appViewSegment",["$route","$compile","$controller","$routeParams","$routeSegment","$q","$injector","$timeout",function(b,c,d,e,f,g,h,i){return{restrict:"ECA",priority:500,compile:function(b,e){var g=b.html(),j=!0,k=a.element(document.createComment(" view-segment "));return b.prepend(k),function(l){function m(){p&&(r.leave(p),p=null),o&&(o.$destroy(),o=null)}function n(e){if(q=e,j&&(j=!1,b.replaceWith(k)),!e)return m(),p=b.clone(),p.html(g),r.enter(p,null,k),void c(p,!1,499)(l);var f=a.extend({},e.locals),h=f&&f.$template;m(),p=b.clone(),p.html(h?h:g),r.enter(p,null,k);var i,n=c(p,!1,499);o=l.$new(),e.params.controller&&(f.$scope=o,i=d(e.params.controller,f),e.params.controllerAs&&(o[e.params.controllerAs]=i),p.data("$ngControllerController",i),p.children().data("$ngControllerController",i)),n(o),o.$emit("$viewContentLoaded"),o.$eval(t)}var o,p,q,r,s,t=e.onload||"",u=parseInt(e.appViewSegment);try{var v=h.get("$animator");r=v(l,e)}catch(w){}try{r=h.get("$animate")}catch(w){}f.chain[u]&&(s=i(function(){n(f.chain[u])},0)),l.$on("routeSegmentChange",function(a,b){s&&i.cancel(s),b.index==u&&q!=b.segment&&n(b.segment)})}}}}])}(angular);
\ No newline at end of file
diff --git a/package.json b/package.json
index c670b20..8a4cc0c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "angular-route-segment",
- "version": "1.2.4",
+ "version": "1.3.0",
"dependencies": {
"grunt": "",
"grunt-contrib-uglify": "",