mirror of
https://github.com/wahyd4/Libr-mobile.git
synced 2026-08-09 04:45:57 +10:00
try to apply new project structure
This commit is contained in:
+8
-2
@@ -4,8 +4,6 @@
|
||||
/bower_components/**
|
||||
/out/**
|
||||
|
||||
/www/assets/**
|
||||
/www/dist/**
|
||||
|
||||
/platforms/android/bin/**
|
||||
/platforms/android/out/**
|
||||
@@ -18,3 +16,11 @@
|
||||
|
||||
/platforms/ios/cordova/console.log
|
||||
/platforms/android/cordova/console.log
|
||||
|
||||
/dist
|
||||
/www/assets/**
|
||||
/www/dist/**
|
||||
/www/img/**
|
||||
/www/js/**
|
||||
/www/templates/**
|
||||
/www/index.html
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "angular-animate",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular-animate.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.16"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-animate",
|
||||
"_release": "1.2.16",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.16",
|
||||
"commit": "4eccd8ec8356a33bf3a98958d7a73b71290d3985"
|
||||
},
|
||||
"_source": "git://github.com/angular/bower-angular-animate.git",
|
||||
"_target": "~1.2.12",
|
||||
"_originalSource": "angular-animate"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# bower-angular-animate
|
||||
|
||||
This repo is for distribution on `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngAnimate).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
Install with `bower`:
|
||||
|
||||
```shell
|
||||
bower install angular-animate
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-animate/angular-animate.js"></script>
|
||||
```
|
||||
|
||||
And add `ngAnimate` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngAnimate']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngAnimate).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+1616
File diff suppressed because it is too large
Load Diff
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
AngularJS v1.2.16
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(s,g,P){'use strict';g.module("ngAnimate",["ng"]).factory("$$animateReflow",["$$rAF","$document",function(g,s){return function(e){return g(function(){e()})}}]).config(["$provide","$animateProvider",function(ga,G){function e(e){for(var p=0;p<e.length;p++){var g=e[p];if(g.nodeType==ba)return g}}function u(p){return g.element(e(p))}var m=g.noop,p=g.forEach,Q=G.$$selectors,ba=1,h="$$ngAnimateState",J="ng-animate",r={running:!0};ga.decorator("$animate",["$delegate","$injector","$sniffer","$rootElement",
|
||||
"$$asyncCallback","$rootScope","$document",function(t,s,aa,K,E,H,P){function R(a){if(a){var b=[],c={};a=a.substr(1).split(".");(aa.transitions||aa.animations)&&b.push(s.get(Q[""]));for(var d=0;d<a.length;d++){var f=a[d],e=Q[f];e&&!c[f]&&(b.push(s.get(e)),c[f]=!0)}return b}}function L(a,b,c){function d(a,b){var c=a[b],d=a["before"+b.charAt(0).toUpperCase()+b.substr(1)];if(c||d)return"leave"==b&&(d=c,c=null),A.push({event:b,fn:c}),l.push({event:b,fn:d}),!0}function f(b,d,e){var f=[];p(b,function(a){a.fn&&
|
||||
f.push(a)});var n=0;p(f,function(b,p){var C=function(){a:{if(d){(d[p]||m)();if(++n<f.length)break a;d=null}e()}};switch(b.event){case "setClass":d.push(b.fn(a,q,y,C));break;case "addClass":d.push(b.fn(a,q||c,C));break;case "removeClass":d.push(b.fn(a,y||c,C));break;default:d.push(b.fn(a,C))}});d&&0===d.length&&e()}var e=a[0];if(e){var h="setClass"==b,r=h||"addClass"==b||"removeClass"==b,q,y;g.isArray(c)&&(q=c[0],y=c[1],c=q+" "+y);var z=a.attr("class")+" "+c;if(T(z)){var v=m,w=[],l=[],x=m,n=[],A=[],
|
||||
z=(" "+z).replace(/\s+/g,".");p(R(z),function(a){!d(a,b)&&h&&(d(a,"addClass"),d(a,"removeClass"))});return{node:e,event:b,className:c,isClassBased:r,isSetClassOperation:h,before:function(a){v=a;f(l,w,function(){v=m;a()})},after:function(a){x=a;f(A,n,function(){x=m;a()})},cancel:function(){w&&(p(w,function(a){(a||m)(!0)}),v(!0));n&&(p(n,function(a){(a||m)(!0)}),x(!0))}}}}}function F(a,b,c,d,f,e,r){function m(d){var e="$animate:"+d;x&&(x[e]&&0<x[e].length)&&E(function(){c.triggerHandler(e,{event:a,
|
||||
className:b})})}function q(){m("before")}function y(){m("after")}function z(){m("close");r&&E(function(){r()})}function v(){v.hasBeenRun||(v.hasBeenRun=!0,e())}function w(){if(!w.hasBeenRun){w.hasBeenRun=!0;var d=c.data(h);d&&(l&&l.isClassBased?B(c,b):(E(function(){var d=c.data(h)||{};t==d.index&&B(c,b,a)}),c.data(h,d)));z()}}var l=L(c,a,b);if(l){b=l.className;var x=g.element._data(l.node),x=x&&x.events;d||(d=f?f.parent():c.parent());var n=c.data(h)||{};f=n.active||{};var A=n.totalActive||0,C=n.last;
|
||||
if(l.isClassBased&&(n.disabled||C&&!C.isClassBased)||N(c,d))v(),q(),y(),w();else{d=!1;if(0<A){n=[];if(l.isClassBased)"setClass"==C.event?(n.push(C),B(c,b)):f[b]&&(s=f[b],s.event==a?d=!0:(n.push(s),B(c,b)));else if("leave"==a&&f["ng-leave"])d=!0;else{for(var s in f)n.push(f[s]),B(c,s);f={};A=0}0<n.length&&p(n,function(a){a.cancel()})}!l.isClassBased||(l.isSetClassOperation||d)||(d="addClass"==a==c.hasClass(b));if(d)q(),y(),z();else{if("leave"==a)c.one("$destroy",function(a){a=g.element(this);var b=
|
||||
a.data(h);b&&(b=b.active["ng-leave"])&&(b.cancel(),B(a,"ng-leave"))});c.addClass(J);var t=O++;A++;f[b]=l;c.data(h,{last:l,active:f,index:t,totalActive:A});q();l.before(function(d){var e=c.data(h);d=d||!e||!e.active[b]||l.isClassBased&&e.active[b].event!=a;v();!0===d?w():(y(),l.after(w))})}}}else v(),q(),y(),w()}function U(a){if(a=e(a))a=g.isFunction(a.getElementsByClassName)?a.getElementsByClassName(J):a.querySelectorAll("."+J),p(a,function(a){a=g.element(a);(a=a.data(h))&&a.active&&p(a.active,function(a){a.cancel()})})}
|
||||
function B(a,b){if(e(a)==e(K))r.disabled||(r.running=!1,r.structural=!1);else if(b){var c=a.data(h)||{},d=!0===b;!d&&(c.active&&c.active[b])&&(c.totalActive--,delete c.active[b]);if(d||!c.totalActive)a.removeClass(J),a.removeData(h)}}function N(a,b){if(r.disabled)return!0;if(e(a)==e(K))return r.disabled||r.running;do{if(0===b.length)break;var c=e(b)==e(K),d=c?r:b.data(h),d=d&&(!!d.disabled||d.running||0<d.totalActive);if(c||d)return d;if(c)break}while(b=b.parent());return!0}var O=0;K.data(h,r);H.$$postDigest(function(){H.$$postDigest(function(){r.running=
|
||||
!1})});var V=G.classNameFilter(),T=V?function(a){return V.test(a)}:function(){return!0};return{enter:function(a,b,c,d){this.enabled(!1,a);t.enter(a,b,c);H.$$postDigest(function(){a=u(a);F("enter","ng-enter",a,b,c,m,d)})},leave:function(a,b){U(a);this.enabled(!1,a);H.$$postDigest(function(){F("leave","ng-leave",u(a),null,null,function(){t.leave(a)},b)})},move:function(a,b,c,d){U(a);this.enabled(!1,a);t.move(a,b,c);H.$$postDigest(function(){a=u(a);F("move","ng-move",a,b,c,m,d)})},addClass:function(a,
|
||||
b,c){a=u(a);F("addClass",b,a,null,null,function(){t.addClass(a,b)},c)},removeClass:function(a,b,c){a=u(a);F("removeClass",b,a,null,null,function(){t.removeClass(a,b)},c)},setClass:function(a,b,c,d){a=u(a);F("setClass",[b,c],a,null,null,function(){t.setClass(a,b,c)},d)},enabled:function(a,b){switch(arguments.length){case 2:if(a)B(b);else{var c=b.data(h)||{};c.disabled=!0;b.data(h,c)}break;case 1:r.disabled=!a;break;default:a=!r.disabled}return!!a}}}]);G.register("",["$window","$sniffer","$timeout",
|
||||
"$$animateReflow",function(h,r,u,K){function E(a,k){S&&S();W.push(k);S=K(function(){p(W,function(a){a()});W=[];S=null;M={}})}function H(a,k){var b=e(a);a=g.element(b);Y.push(a);b=Date.now()+k;b<=fa||(u.cancel(ea),fa=b,ea=u(function(){J(Y);Y=[]},k,!1))}function J(a){p(a,function(a){(a=a.data(n))&&(a.closeAnimationFn||m)()})}function R(a,k){var b=k?M[k]:null;if(!b){var c=0,d=0,e=0,f=0,n,Z,$,g;p(a,function(a){if(a.nodeType==ba){a=h.getComputedStyle(a)||{};$=a[I+z];c=Math.max(L($),c);g=a[I+v];n=a[I+w];
|
||||
d=Math.max(L(n),d);Z=a[q+w];f=Math.max(L(Z),f);var k=L(a[q+z]);0<k&&(k*=parseInt(a[q+l],10)||1);e=Math.max(k,e)}});b={total:0,transitionPropertyStyle:g,transitionDurationStyle:$,transitionDelayStyle:n,transitionDelay:d,transitionDuration:c,animationDelayStyle:Z,animationDelay:f,animationDuration:e};k&&(M[k]=b)}return b}function L(a){var k=0;a=g.isString(a)?a.split(/\s*,\s*/):[];p(a,function(a){k=Math.max(parseFloat(a)||0,k)});return k}function F(a){var k=a.parent(),b=k.data(x);b||(k.data(x,++da),
|
||||
b=da);return b+"-"+e(a).getAttribute("class")}function U(a,k,b,c){var d=F(k),f=d+" "+b,p=M[f]?++M[f].total:0,g={};if(0<p){var h=b+"-stagger",g=d+" "+h;(d=!M[g])&&k.addClass(h);g=R(k,g);d&&k.removeClass(h)}c=c||function(a){return a()};k.addClass(b);var h=k.data(n)||{},l=c(function(){return R(k,f)});c=l.transitionDuration;d=l.animationDuration;if(0===c&&0===d)return k.removeClass(b),!1;k.data(n,{running:h.running||0,itemIndex:p,stagger:g,timings:l,closeAnimationFn:m});a=0<h.running||"setClass"==a;0<
|
||||
c&&B(k,b,a);0<d&&(0<g.animationDelay&&0===g.animationDuration)&&(e(k).style[q]="none 0s");return!0}function B(a,b,c){"ng-enter"!=b&&("ng-move"!=b&&"ng-leave"!=b)&&c?a.addClass(A):e(a).style[I+v]="none"}function N(a,b){var c=I+v,d=e(a);d.style[c]&&0<d.style[c].length&&(d.style[c]="");a.removeClass(A)}function O(a){var b=q;a=e(a);a.style[b]&&0<a.style[b].length&&(a.style[b]="")}function V(a,b,c,f){function g(a){b.off(z,h);b.removeClass(r);d(b,c);a=e(b);for(var X in u)a.style.removeProperty(u[X])}function h(a){a.stopPropagation();
|
||||
var b=a.originalEvent||a;a=b.$manualTimeStamp||b.timeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(C));Math.max(a-A,0)>=x&&b>=v&&f()}var l=e(b);a=b.data(n);if(-1!=l.getAttribute("class").indexOf(c)&&a){var r="";p(c.split(" "),function(a,b){r+=(0<b?" ":"")+a+"-active"});var q=a.stagger,m=a.timings,s=a.itemIndex,v=Math.max(m.transitionDuration,m.animationDuration),w=Math.max(m.transitionDelay,m.animationDelay),x=w*ca,A=Date.now(),z=y+" "+G,t="",u=[];if(0<m.transitionDuration){var B=m.transitionPropertyStyle;
|
||||
-1==B.indexOf("all")&&(t+=D+"transition-property: "+B+";",t+=D+"transition-duration: "+m.transitionDurationStyle+";",u.push(D+"transition-property"),u.push(D+"transition-duration"))}0<s&&(0<q.transitionDelay&&0===q.transitionDuration&&(t+=D+"transition-delay: "+T(m.transitionDelayStyle,q.transitionDelay,s)+"; ",u.push(D+"transition-delay")),0<q.animationDelay&&0===q.animationDuration&&(t+=D+"animation-delay: "+T(m.animationDelayStyle,q.animationDelay,s)+"; ",u.push(D+"animation-delay")));0<u.length&&
|
||||
(m=l.getAttribute("style")||"",l.setAttribute("style",m+" "+t));b.on(z,h);b.addClass(r);a.closeAnimationFn=function(){g();f()};l=(s*(Math.max(q.animationDelay,q.transitionDelay)||0)+(w+v)*Q)*ca;a.running++;H(b,l);return g}f()}function T(a,b,c){var d="";p(a.split(","),function(a,X){d+=(0<X?",":"")+(c*b+parseInt(a,10))+"s"});return d}function a(a,b,c,e){if(U(a,b,c,e))return function(a){a&&d(b,c)}}function b(a,b,c,e){if(b.data(n))return V(a,b,c,e);d(b,c);e()}function c(c,d,e,f){var g=a(c,d,e);if(g){var h=
|
||||
g;E(d,function(){N(d,e);O(d);h=b(c,d,e,f)});return function(a){(h||m)(a)}}f()}function d(a,b){a.removeClass(b);var c=a.data(n);c&&(c.running&&c.running--,c.running&&0!==c.running||a.removeData(n))}function f(a,b){var c="";a=g.isArray(a)?a:a.split(/\s+/);p(a,function(a,d){a&&0<a.length&&(c+=(0<d?" ":"")+a+b)});return c}var D="",I,G,q,y;s.ontransitionend===P&&s.onwebkittransitionend!==P?(D="-webkit-",I="WebkitTransition",G="webkitTransitionEnd transitionend"):(I="transition",G="transitionend");s.onanimationend===
|
||||
P&&s.onwebkitanimationend!==P?(D="-webkit-",q="WebkitAnimation",y="webkitAnimationEnd animationend"):(q="animation",y="animationend");var z="Duration",v="Property",w="Delay",l="IterationCount",x="$$ngAnimateKey",n="$$ngAnimateCSS3Data",A="ng-animate-block-transitions",C=3,Q=1.5,ca=1E3,M={},da=0,W=[],S,ea=null,fa=0,Y=[];return{enter:function(a,b){return c("enter",a,"ng-enter",b)},leave:function(a,b){return c("leave",a,"ng-leave",b)},move:function(a,b){return c("move",a,"ng-move",b)},beforeSetClass:function(b,
|
||||
c,d,e){var g=f(d,"-remove")+" "+f(c,"-add"),h=a("setClass",b,g,function(a){var e=b.attr("class");b.removeClass(d);b.addClass(c);a=a();b.attr("class",e);return a});if(h)return E(b,function(){N(b,g);O(b);e()}),h;e()},beforeAddClass:function(b,c,d){var e=a("addClass",b,f(c,"-add"),function(a){b.addClass(c);a=a();b.removeClass(c);return a});if(e)return E(b,function(){N(b,c);O(b);d()}),e;d()},setClass:function(a,c,d,e){d=f(d,"-remove");c=f(c,"-add");return b("setClass",a,d+" "+c,e)},addClass:function(a,
|
||||
c,d){return b("addClass",a,f(c,"-add"),d)},beforeRemoveClass:function(b,c,d){var e=a("removeClass",b,f(c,"-remove"),function(a){var d=b.attr("class");b.removeClass(c);a=a();b.attr("class",d);return a});if(e)return E(b,function(){N(b,c);O(b);d()}),e;d()},removeClass:function(a,c,d){return b("removeClass",a,f(c,"-remove"),d)}}}])}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-animate.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "angular-animate",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular-animate.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.16"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "angular-resource",
|
||||
"version": "1.2.17-build.104+sha.e987efd",
|
||||
"main": "./angular-resource.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.17-build.104+sha.e987efd"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-resource",
|
||||
"_release": "1.2.17-build.104+sha.e987efd",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.17-build.104+sha.e987efd",
|
||||
"commit": "ea731e6e83f0ef97260b288cb64d4ac33a6e927b"
|
||||
},
|
||||
"_source": "git://github.com/angular/bower-angular-resource.git",
|
||||
"_target": "~1.2.17",
|
||||
"_originalSource": "angular-resource"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# bower-angular-resource
|
||||
|
||||
This repo is for distribution on `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngResource).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
Install with `bower`:
|
||||
|
||||
```shell
|
||||
bower install angular-resource
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-resource/angular-resource.js"></script>
|
||||
```
|
||||
|
||||
And add `ngResource` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngResource']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngResource).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+610
@@ -0,0 +1,610 @@
|
||||
/**
|
||||
* @license AngularJS v1.2.17-build.104+sha.e987efd
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
var $resourceMinErr = angular.$$minErr('$resource');
|
||||
|
||||
// Helper functions and regex to lookup a dotted path on an object
|
||||
// stopping at undefined/null. The path must be composed of ASCII
|
||||
// identifiers (just like $parse)
|
||||
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
|
||||
|
||||
function isValidDottedPath(path) {
|
||||
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
|
||||
MEMBER_NAME_REGEX.test('.' + path));
|
||||
}
|
||||
|
||||
function lookupDottedPath(obj, path) {
|
||||
if (!isValidDottedPath(path)) {
|
||||
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
|
||||
}
|
||||
var keys = path.split('.');
|
||||
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
|
||||
var key = keys[i];
|
||||
obj = (obj !== null) ? obj[key] : undefined;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shallow copy of an object and clear other fields from the destination
|
||||
*/
|
||||
function shallowClearAndCopy(src, dst) {
|
||||
dst = dst || {};
|
||||
|
||||
angular.forEach(dst, function(value, key){
|
||||
delete dst[key];
|
||||
});
|
||||
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngResource
|
||||
* @description
|
||||
*
|
||||
* # ngResource
|
||||
*
|
||||
* The `ngResource` module provides interaction support with RESTful services
|
||||
* via the $resource service.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngResource"></div>
|
||||
*
|
||||
* See {@link ngResource.$resource `$resource`} for usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $resource
|
||||
* @requires $http
|
||||
*
|
||||
* @description
|
||||
* A factory which creates a resource object that lets you interact with
|
||||
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
|
||||
*
|
||||
* The returned resource object has action methods which provide high-level behaviors without
|
||||
* the need to interact with the low level {@link ng.$http $http} service.
|
||||
*
|
||||
* Requires the {@link ngResource `ngResource`} module to be installed.
|
||||
*
|
||||
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `http://example.com:8080/api`), it will be respected.
|
||||
*
|
||||
* If you are using a url with a suffix, just add the suffix, like this:
|
||||
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
|
||||
* or even `$resource('http://example.com/resource/:resource_id.:format')`
|
||||
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
|
||||
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
|
||||
* can escape it with `/\.`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods. If any of the parameter value is a function, it will be executed every time
|
||||
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from
|
||||
* the data object (useful for non-GET operations).
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
* ng.$http#usage_parameters $http.config}:
|
||||
*
|
||||
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* action2: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* ...}
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* - **`action`** – {string} – The name of action. This name becomes the name of the method on
|
||||
* your resource object.
|
||||
* - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`,
|
||||
* `DELETE`, and `JSONP`.
|
||||
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
|
||||
* the parameter value is a function, it will be executed every time when a param value needs to
|
||||
* be obtained for a request (unless the param was overridden).
|
||||
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
|
||||
* like for the resource-level urls.
|
||||
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
|
||||
* see `returns` section.
|
||||
* - **`transformRequest`** –
|
||||
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* request body and headers and returns its transformed (typically serialized) version.
|
||||
* - **`transformResponse`** –
|
||||
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body and headers and returns its transformed (typically deserialized) version.
|
||||
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
* caching.
|
||||
* - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
|
||||
* should abort the request when resolved.
|
||||
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
* XHR object. See
|
||||
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
|
||||
* for more information.
|
||||
* - **`responseType`** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
|
||||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
|
||||
* with `http response` object. See {@link ng.$http $http interceptors}.
|
||||
*
|
||||
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||
* optionally extended with custom `actions`. The default set contains these actions:
|
||||
* ```js
|
||||
* { 'get': {method:'GET'},
|
||||
* 'save': {method:'POST'},
|
||||
* 'query': {method:'GET', isArray:true},
|
||||
* 'remove': {method:'DELETE'},
|
||||
* 'delete': {method:'DELETE'} };
|
||||
* ```
|
||||
*
|
||||
* Calling these methods invoke an {@link ng.$http} with the specified http method,
|
||||
* destination and parameters. When the data is returned from the server then the object is an
|
||||
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
|
||||
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
||||
* read, update, delete) on server-side data like this:
|
||||
* ```js
|
||||
* var User = $resource('/user/:userId', {userId:'@id'});
|
||||
* var user = User.get({userId:123}, function() {
|
||||
* user.abc = true;
|
||||
* user.$save();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* It is important to realize that invoking a $resource object method immediately returns an
|
||||
* empty reference (object or array depending on `isArray`). Once the data is returned from the
|
||||
* server the existing reference is populated with the actual data. This is a useful trick since
|
||||
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
|
||||
* object results in no rendering, once the data arrives from the server then the object is
|
||||
* populated with the data and the view automatically re-renders itself showing the new data. This
|
||||
* means that in most cases one never has to write a callback function for the action methods.
|
||||
*
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
|
||||
* with (httpResponse) argument.
|
||||
*
|
||||
* Class actions return empty instance (with additional properties below).
|
||||
* Instance actions return promise of the action.
|
||||
*
|
||||
* The Resource instances and collection have these additional properties:
|
||||
*
|
||||
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
|
||||
* instance or collection.
|
||||
*
|
||||
* On success, the promise is resolved with the same resource instance or collection object,
|
||||
* updated with data from server. This makes it easy to use in
|
||||
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
|
||||
* rendering until the resource(s) are loaded.
|
||||
*
|
||||
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
|
||||
* the `resource` property.
|
||||
*
|
||||
* If an interceptor object was provided, the promise will instead be resolved with the value
|
||||
* returned by the interceptor.
|
||||
*
|
||||
* - `$resolved`: `true` after first server interaction is completed (either with success or
|
||||
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
|
||||
* data-binding.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
*
|
||||
* ```js
|
||||
// Define CreditCard class
|
||||
var CreditCard = $resource('/user/:userId/card/:cardId',
|
||||
{userId:123, cardId:'@id'}, {
|
||||
charge: {method:'POST', params:{charge:true}}
|
||||
});
|
||||
|
||||
// We can retrieve a collection from the server
|
||||
var cards = CreditCard.query(function() {
|
||||
// GET: /user/123/card
|
||||
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
|
||||
|
||||
var card = cards[0];
|
||||
// each item is an instance of CreditCard
|
||||
expect(card instanceof CreditCard).toEqual(true);
|
||||
card.name = "J. Smith";
|
||||
// non GET methods are mapped onto the instances
|
||||
card.$save();
|
||||
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
|
||||
// server returns: {id:456, number:'1234', name: 'J. Smith'};
|
||||
|
||||
// our custom method is mapped as well.
|
||||
card.$charge({amount:9.99});
|
||||
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
|
||||
});
|
||||
|
||||
// we can create an instance as well
|
||||
var newCard = new CreditCard({number:'0123'});
|
||||
newCard.name = "Mike Smith";
|
||||
newCard.$save();
|
||||
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
|
||||
expect(newCard.id).toEqual(789);
|
||||
* ```
|
||||
*
|
||||
* The object returned from this function execution is a resource "class" which has "static" method
|
||||
* for each action in the definition.
|
||||
*
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
|
||||
* `headers`.
|
||||
* When the data is returned from the server then the object is an instance of the resource type and
|
||||
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||
* operations (create, read, update, delete) on server-side data.
|
||||
|
||||
```js
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(user) {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
```
|
||||
*
|
||||
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
|
||||
* in the response that came from the server as well as $http header getter function, so one
|
||||
* could rewrite the above example and get access to http headers as:
|
||||
*
|
||||
```js
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(u, getResponseHeaders){
|
||||
u.abc = true;
|
||||
u.$save(function(u, putResponseHeaders) {
|
||||
//u => saved user object
|
||||
//putResponseHeaders => $http header getter
|
||||
});
|
||||
});
|
||||
```
|
||||
*
|
||||
* You can also access the raw `$http` promise via the `$promise` property on the object returned
|
||||
*
|
||||
```
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123})
|
||||
.$promise.then(function(user) {
|
||||
$scope.user = user;
|
||||
});
|
||||
```
|
||||
|
||||
* # Creating a custom 'PUT' request
|
||||
* In this example we create a custom method on our resource to make a PUT request
|
||||
* ```js
|
||||
* var app = angular.module('app', ['ngResource', 'ngRoute']);
|
||||
*
|
||||
* // Some APIs expect a PUT request in the format URL/object/ID
|
||||
* // Here we are creating an 'update' method
|
||||
* app.factory('Notes', ['$resource', function($resource) {
|
||||
* return $resource('/notes/:id', null,
|
||||
* {
|
||||
* 'update': { method:'PUT' }
|
||||
* });
|
||||
* }]);
|
||||
*
|
||||
* // In our controller we get the ID from the URL using ngRoute and $routeParams
|
||||
* // We pass in $routeParams and our Notes factory along with $scope
|
||||
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
|
||||
function($scope, $routeParams, Notes) {
|
||||
* // First get a note object from the factory
|
||||
* var note = Notes.get({ id:$routeParams.id });
|
||||
* $id = note.id;
|
||||
*
|
||||
* // Now call update passing in the ID first then the object you are updating
|
||||
* Notes.update({ id:$id }, note);
|
||||
*
|
||||
* // This will PUT /notes/ID with the note object in the request payload
|
||||
* }]);
|
||||
* ```
|
||||
*/
|
||||
angular.module('ngResource', ['ng']).
|
||||
factory('$resource', ['$http', '$q', function($http, $q) {
|
||||
|
||||
var DEFAULT_ACTIONS = {
|
||||
'get': {method:'GET'},
|
||||
'save': {method:'POST'},
|
||||
'query': {method:'GET', isArray:true},
|
||||
'remove': {method:'DELETE'},
|
||||
'delete': {method:'DELETE'}
|
||||
};
|
||||
var noop = angular.noop,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
isFunction = angular.isFunction;
|
||||
|
||||
/**
|
||||
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriSegment(val) {
|
||||
return encodeUriQuery(val, true).
|
||||
replace(/%26/gi, '&').
|
||||
replace(/%3D/gi, '=').
|
||||
replace(/%2B/gi, '+');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a
|
||||
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
|
||||
* have to be encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template;
|
||||
this.defaults = defaults || {};
|
||||
this.urlParams = {};
|
||||
}
|
||||
|
||||
Route.prototype = {
|
||||
setUrlParams: function(config, params, actionUrl) {
|
||||
var self = this,
|
||||
url = actionUrl || self.template,
|
||||
val,
|
||||
encodedVal;
|
||||
|
||||
var urlParams = self.urlParams = {};
|
||||
forEach(url.split(/\W/), function(param){
|
||||
if (param === 'hasOwnProperty') {
|
||||
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
|
||||
}
|
||||
if (!(new RegExp("^\\d+$").test(param)) && param &&
|
||||
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
});
|
||||
url = url.replace(/\\:/g, ':');
|
||||
|
||||
params = params || {};
|
||||
forEach(self.urlParams, function(_, urlParam){
|
||||
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
|
||||
if (angular.isDefined(val) && val !== null) {
|
||||
encodedVal = encodeUriSegment(val);
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
|
||||
return encodedVal + p1;
|
||||
});
|
||||
} else {
|
||||
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
|
||||
leadingSlashes, tail) {
|
||||
if (tail.charAt(0) == '/') {
|
||||
return tail;
|
||||
} else {
|
||||
return leadingSlashes + tail;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// strip trailing slashes and set the url
|
||||
url = url.replace(/\/+$/, '') || '/';
|
||||
// then replace collapse `/.` if found in the last URL path segment before the query
|
||||
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
|
||||
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
|
||||
// replace escaped `/\.` with `/.`
|
||||
config.url = url.replace(/\/\\\./, '/.');
|
||||
|
||||
|
||||
// set params - delegate param encoding to $http
|
||||
forEach(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
config.params = config.params || {};
|
||||
config.params[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function resourceFactory(url, paramDefaults, actions) {
|
||||
var route = new Route(url);
|
||||
|
||||
actions = extend({}, DEFAULT_ACTIONS, actions);
|
||||
|
||||
function extractParams(data, actionParams){
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key){
|
||||
if (isFunction(value)) { value = value(); }
|
||||
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
|
||||
lookupDottedPath(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function defaultResponseInterceptor(response) {
|
||||
return response.resource;
|
||||
}
|
||||
|
||||
function Resource(value){
|
||||
shallowClearAndCopy(value || {}, this);
|
||||
}
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
|
||||
|
||||
Resource[name] = function(a1, a2, a3, a4) {
|
||||
var params = {}, data, success, error;
|
||||
|
||||
/* jshint -W086 */ /* (purposefully fall through case statements) */
|
||||
switch(arguments.length) {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
success = a3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (isFunction(a1)) success = a1;
|
||||
else if (hasBody) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
throw $resourceMinErr('badargs',
|
||||
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
|
||||
arguments.length);
|
||||
}
|
||||
/* jshint +W086 */ /* (purposefully fall through case statements) */
|
||||
|
||||
var isInstanceCall = this instanceof Resource;
|
||||
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
|
||||
var httpConfig = {};
|
||||
var responseInterceptor = action.interceptor && action.interceptor.response ||
|
||||
defaultResponseInterceptor;
|
||||
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
|
||||
undefined;
|
||||
|
||||
forEach(action, function(value, key) {
|
||||
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
|
||||
httpConfig[key] = copy(value);
|
||||
}
|
||||
});
|
||||
|
||||
if (hasBody) httpConfig.data = data;
|
||||
route.setUrlParams(httpConfig,
|
||||
extend({}, extractParams(data, action.params || {}), params),
|
||||
action.url);
|
||||
|
||||
var promise = $http(httpConfig).then(function(response) {
|
||||
var data = response.data,
|
||||
promise = value.$promise;
|
||||
|
||||
if (data) {
|
||||
// Need to convert action.isArray to boolean in case it is undefined
|
||||
// jshint -W018
|
||||
if (angular.isArray(data) !== (!!action.isArray)) {
|
||||
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
|
||||
'response to contain an {0} but got an {1}',
|
||||
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
|
||||
}
|
||||
// jshint +W018
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
shallowClearAndCopy(data, value);
|
||||
value.$promise = promise;
|
||||
}
|
||||
}
|
||||
|
||||
value.$resolved = true;
|
||||
|
||||
response.resource = value;
|
||||
|
||||
return response;
|
||||
}, function(response) {
|
||||
value.$resolved = true;
|
||||
|
||||
(error||noop)(response);
|
||||
|
||||
return $q.reject(response);
|
||||
});
|
||||
|
||||
promise = promise.then(
|
||||
function(response) {
|
||||
var value = responseInterceptor(response);
|
||||
(success||noop)(value, response.headers);
|
||||
return value;
|
||||
},
|
||||
responseErrorInterceptor);
|
||||
|
||||
if (!isInstanceCall) {
|
||||
// we are creating instance / collection
|
||||
// - set the initial promise
|
||||
// - return the instance / collection
|
||||
value.$promise = promise;
|
||||
value.$resolved = false;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// instance call
|
||||
return promise;
|
||||
};
|
||||
|
||||
|
||||
Resource.prototype['$' + name] = function(params, success, error) {
|
||||
if (isFunction(params)) {
|
||||
error = success; success = params; params = {};
|
||||
}
|
||||
var result = Resource[name].call(this, params, this, success, error);
|
||||
return result.$promise || result;
|
||||
};
|
||||
});
|
||||
|
||||
Resource.bind = function(additionalParamDefaults){
|
||||
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
return Resource;
|
||||
}
|
||||
|
||||
return resourceFactory;
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
AngularJS v1.2.17-build.104+sha.e987efd
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(H,a,A){'use strict';function D(p,g){g=g||{};a.forEach(g,function(a,c){delete g[c]});for(var c in p)!p.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(g[c]=p[c]);return g}var v=a.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;a.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(p,g){function c(a,c){this.template=a;this.defaults=c||{};this.urlParams={}}function t(n,w,l){function r(h,d){var e={};d=x({},w,d);s(d,function(b,d){u(b)&&(b=b());var k;if(b&&
|
||||
b.charAt&&"@"==b.charAt(0)){k=h;var a=b.substr(1);if(null==a||""===a||"hasOwnProperty"===a||!C.test("."+a))throw v("badmember",a);for(var a=a.split("."),f=0,c=a.length;f<c&&k!==A;f++){var g=a[f];k=null!==k?k[g]:A}}else k=b;e[d]=k});return e}function e(a){return a.resource}function f(a){D(a||{},this)}var F=new c(n);l=x({},B,l);s(l,function(h,d){var c=/^(POST|PUT|PATCH)$/i.test(h.method);f[d]=function(b,d,k,w){var q={},n,l,y;switch(arguments.length){case 4:y=w,l=k;case 3:case 2:if(u(d)){if(u(b)){l=
|
||||
b;y=d;break}l=d;y=k}else{q=b;n=d;l=k;break}case 1:u(b)?l=b:c?n=b:q=b;break;case 0:break;default:throw v("badargs",arguments.length);}var t=this instanceof f,m=t?n:h.isArray?[]:new f(n),z={},B=h.interceptor&&h.interceptor.response||e,C=h.interceptor&&h.interceptor.responseError||A;s(h,function(a,b){"params"!=b&&("isArray"!=b&&"interceptor"!=b)&&(z[b]=G(a))});c&&(z.data=n);F.setUrlParams(z,x({},r(n,h.params||{}),q),h.url);q=p(z).then(function(b){var d=b.data,k=m.$promise;if(d){if(a.isArray(d)!==!!h.isArray)throw v("badcfg",
|
||||
h.isArray?"array":"object",a.isArray(d)?"array":"object");h.isArray?(m.length=0,s(d,function(b){m.push(new f(b))})):(D(d,m),m.$promise=k)}m.$resolved=!0;b.resource=m;return b},function(b){m.$resolved=!0;(y||E)(b);return g.reject(b)});q=q.then(function(b){var a=B(b);(l||E)(a,b.headers);return a},C);return t?q:(m.$promise=q,m.$resolved=!1,m)};f.prototype["$"+d]=function(b,a,k){u(b)&&(k=a,a=b,b={});b=f[d].call(this,b,this,a,k);return b.$promise||b}});f.bind=function(a){return t(n,x({},w,a),l)};return f}
|
||||
var B={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},E=a.noop,s=a.forEach,x=a.extend,G=a.copy,u=a.isFunction;c.prototype={setUrlParams:function(c,g,l){var r=this,e=l||r.template,f,p,h=r.urlParams={};s(e.split(/\W/),function(a){if("hasOwnProperty"===a)throw v("badname");!/^\d+$/.test(a)&&(a&&RegExp("(^|[^\\\\]):"+a+"(\\W|$)").test(e))&&(h[a]=!0)});e=e.replace(/\\:/g,":");g=g||{};s(r.urlParams,function(d,c){f=g.hasOwnProperty(c)?
|
||||
g[c]:r.defaults[c];a.isDefined(f)&&null!==f?(p=encodeURIComponent(f).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+c+"(\\W|$)","g"),function(a,c){return p+c})):e=e.replace(RegExp("(/?):"+c+"(\\W|$)","g"),function(a,c,d){return"/"==d.charAt(0)?d:c+d})});e=e.replace(/\/+$/,"")||"/";e=e.replace(/\/\.(?=\w+($|\?))/,".");c.url=e.replace(/\/\\\./,"/.");s(g,function(a,
|
||||
e){r.urlParams[e]||(c.params=c.params||{},c.params[e]=a)})}};return t}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-resource.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "angular-resource",
|
||||
"version": "1.2.17-build.104+sha.e987efd",
|
||||
"main": "./angular-resource.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.17-build.104+sha.e987efd"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "angular-sanitize",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular-sanitize.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.16"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-sanitize",
|
||||
"_release": "1.2.16",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.16",
|
||||
"commit": "0534ccd6bb2b7b6e5d0fa6c27fd3277f312a4347"
|
||||
},
|
||||
"_source": "git://github.com/angular/bower-angular-sanitize.git",
|
||||
"_target": "~1.2.12",
|
||||
"_originalSource": "angular-sanitize"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
# bower-angular-sanitize
|
||||
|
||||
This repo is for distribution on `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngSanitize).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
Install with `bower`:
|
||||
|
||||
```shell
|
||||
bower install angular-sanitize
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-sanitize/angular-sanitize.js"></script>
|
||||
```
|
||||
|
||||
And add `ngSanitize` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngSanitize']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngSanitize).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+624
@@ -0,0 +1,624 @@
|
||||
/**
|
||||
* @license AngularJS v1.2.16
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngSanitize
|
||||
* @description
|
||||
*
|
||||
* # ngSanitize
|
||||
*
|
||||
* The `ngSanitize` module provides functionality to sanitize HTML.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngSanitize"></div>
|
||||
*
|
||||
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
|
||||
*/
|
||||
|
||||
/*
|
||||
* HTML Parser By Misko Hevery (misko@hevery.com)
|
||||
* based on: HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $sanitize
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
|
||||
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
*
|
||||
* @example
|
||||
<example module="ngSanitize" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope, $sce) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
$scope.deliberatelyTrustDangerousSnippet = function() {
|
||||
return $sce.trustAsHtml($scope.snippet);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Directive</td>
|
||||
<td>How</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-sanitize">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Automatically uses $sanitize</td>
|
||||
<td><pre><div ng-bind-html="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind-html="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-trust">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()">
|
||||
</div></pre>
|
||||
</td>
|
||||
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-default">
|
||||
<td>ng-bind</td>
|
||||
<td>Automatically escapes</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should sanitize the html snippet by default', function() {
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should inline raw snippet if bound to a trusted value', function() {
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('new <b>text</b>');
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
|
||||
'new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
|
||||
"new <b onclick=\"alert(1)\">text</b>");
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function $SanitizeProvider() {
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var START_TAG_REGEXP =
|
||||
/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
|
||||
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
|
||||
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
|
||||
BEGIN_TAG_REGEXP = /^</,
|
||||
BEGING_END_TAGE_REGEXP = /^<\s*\//,
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = makeMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = makeMap("rp,rt"),
|
||||
optionalEndTagElements = angular.extend({},
|
||||
optionalEndTagInlineElements,
|
||||
optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
|
||||
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
||||
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
|
||||
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
||||
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var specialElements = makeMap("script,style");
|
||||
|
||||
var validElements = angular.extend({},
|
||||
voidElements,
|
||||
blockElements,
|
||||
inlineElements,
|
||||
optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
|
||||
var validAttrs = angular.extend({}, uriAttrs, makeMap(
|
||||
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
|
||||
'scope,scrolling,shape,size,span,start,summary,target,title,type,'+
|
||||
'valign,value,vspace,width'));
|
||||
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
var index, chars, match, stack = [], last = html;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
while ( html ) {
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if ( !stack.last() || !specialElements[ stack.last() ] ) {
|
||||
|
||||
// Comment
|
||||
if ( html.indexOf("<!--") === 0 ) {
|
||||
// comments containing -- are not allowed unless they terminate the comment
|
||||
index = html.indexOf("--", 4);
|
||||
|
||||
if ( index >= 0 && html.lastIndexOf("-->", index) === index) {
|
||||
if (handler.comment) handler.comment( html.substring( 4, index ) );
|
||||
html = html.substring( index + 3 );
|
||||
chars = false;
|
||||
}
|
||||
// DOCTYPE
|
||||
} else if ( DOCTYPE_REGEXP.test(html) ) {
|
||||
match = html.match( DOCTYPE_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.replace( match[0], '');
|
||||
chars = false;
|
||||
}
|
||||
// end tag
|
||||
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
|
||||
match = html.match( END_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( END_TAG_REGEXP, parseEndTag );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if ( BEGIN_TAG_REGEXP.test(html) ) {
|
||||
match = html.match( START_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( START_TAG_REGEXP, parseStartTag );
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( chars ) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
var text = index < 0 ? html : html.substring( 0, index );
|
||||
html = index < 0 ? "" : html.substring( index );
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
|
||||
function(all, text){
|
||||
text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
|
||||
if ( html == last ) {
|
||||
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
|
||||
"of html: {0}", html);
|
||||
}
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag( tag, tagName, rest, unary ) {
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( blockElements[ tagName ] ) {
|
||||
while ( stack.last() && inlineElements[ stack.last() ] ) {
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) {
|
||||
parseEndTag( "", tagName );
|
||||
}
|
||||
|
||||
unary = voidElements[ tagName ] || !!unary;
|
||||
|
||||
if ( !unary )
|
||||
stack.push( tagName );
|
||||
|
||||
var attrs = {};
|
||||
|
||||
rest.replace(ATTR_REGEXP,
|
||||
function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
|
||||
var value = doubleQuotedValue
|
||||
|| singleQuotedValue
|
||||
|| unquotedValue
|
||||
|| '';
|
||||
|
||||
attrs[name] = decodeEntities(value);
|
||||
});
|
||||
if (handler.start) handler.start( tagName, attrs, unary );
|
||||
}
|
||||
|
||||
function parseEndTag( tag, tagName ) {
|
||||
var pos = 0, i;
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( tagName )
|
||||
// Find the closest opened tag of the same type
|
||||
for ( pos = stack.length - 1; pos >= 0; pos-- )
|
||||
if ( stack[ pos ] == tagName )
|
||||
break;
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
// Close all the open elements, up the stack
|
||||
for ( i = stack.length - 1; i >= pos; i-- )
|
||||
if (handler.end) handler.end( stack[ i ] );
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hiddenPre=document.createElement("pre");
|
||||
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
|
||||
/**
|
||||
* decodes all entities into regular string
|
||||
* @param value
|
||||
* @returns {string} A string with decoded entities.
|
||||
*/
|
||||
function decodeEntities(value) {
|
||||
if (!value) { return ''; }
|
||||
|
||||
// Note: IE8 does not preserve spaces at the start/end of innerHTML
|
||||
// so we must capture them and reattach them afterward
|
||||
var parts = spaceRe.exec(value);
|
||||
var spaceBefore = parts[1];
|
||||
var spaceAfter = parts[3];
|
||||
var content = parts[2];
|
||||
if (content) {
|
||||
hiddenPre.innerHTML=content.replace(/</g,"<");
|
||||
// innerText depends on styling as it doesn't display hidden elements.
|
||||
// Therefore, it's better to use textContent not to cause unnecessary
|
||||
// reflows. However, IE<9 don't support textContent so the innerText
|
||||
// fallback is necessary.
|
||||
content = 'textContent' in hiddenPre ?
|
||||
hiddenPre.textContent : hiddenPre.innerText;
|
||||
}
|
||||
return spaceBefore + content + spaceAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns {string} escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value){
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.jain('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf, uriValidator){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs, unary){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && specialElements[tag]) {
|
||||
ignore = tag;
|
||||
}
|
||||
if (!ignore && validElements[tag] === true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out(unary ? '/>' : '>');
|
||||
}
|
||||
},
|
||||
end: function(tag){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && validElements[tag] === true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignore) {
|
||||
ignore = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars){
|
||||
if (!ignore) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name linky
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plain email address links.
|
||||
*
|
||||
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
|
||||
* @returns {string} Html-linkified text.
|
||||
*
|
||||
* @usage
|
||||
<span ng-bind-html="linky_expression | linky"></span>
|
||||
*
|
||||
* @example
|
||||
<example module="ngSanitize" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'Pretty text with some links:\n'+
|
||||
'http://angularjs.org/,\n'+
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
$scope.snippetWithTarget = 'http://angularjs.org/';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-target">
|
||||
<td>linky target</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithTarget | linky:'_blank'"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
|
||||
});
|
||||
|
||||
it('should not linkify snippet without the linky filter', function() {
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new http://link.');
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('new http://link.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
|
||||
.toBe('new http://link.');
|
||||
});
|
||||
|
||||
it('should work with the target property', function() {
|
||||
expect(element(by.id('linky-target')).
|
||||
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
|
||||
toBe('http://angularjs.org/');
|
||||
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text, target) {
|
||||
if (!text) return text;
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
var url;
|
||||
var i;
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
html.push('<a ');
|
||||
if (angular.isDefined(target)) {
|
||||
html.push('target="');
|
||||
html.push(target);
|
||||
html.push('" ');
|
||||
}
|
||||
html.push('href="');
|
||||
html.push(url);
|
||||
html.push('">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
AngularJS v1.2.16
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(p,h,q){'use strict';function E(a){var e=[];s(e,h.noop).chars(a);return e.join("")}function k(a){var e={};a=a.split(",");var d;for(d=0;d<a.length;d++)e[a[d]]=!0;return e}function F(a,e){function d(a,b,d,g){b=h.lowercase(b);if(t[b])for(;f.last()&&u[f.last()];)c("",f.last());v[b]&&f.last()==b&&c("",b);(g=w[b]||!!g)||f.push(b);var l={};d.replace(G,function(a,b,e,c,d){l[b]=r(e||c||d||"")});e.start&&e.start(b,l,g)}function c(a,b){var c=0,d;if(b=h.lowercase(b))for(c=f.length-1;0<=c&&f[c]!=b;c--);
|
||||
if(0<=c){for(d=f.length-1;d>=c;d--)e.end&&e.end(f[d]);f.length=c}}var b,g,f=[],l=a;for(f.last=function(){return f[f.length-1]};a;){g=!0;if(f.last()&&x[f.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(b,a){a=a.replace(H,"$1").replace(I,"$1");e.chars&&e.chars(r(a));return""}),c("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(e.comment&&e.comment(a.substring(4,b)),a=a.substring(b+3),g=!1);else if(y.test(a)){if(b=a.match(y))a=
|
||||
a.replace(b[0],""),g=!1}else if(J.test(a)){if(b=a.match(z))a=a.substring(b[0].length),b[0].replace(z,c),g=!1}else K.test(a)&&(b=a.match(A))&&(a=a.substring(b[0].length),b[0].replace(A,d),g=!1);g&&(b=a.indexOf("<"),g=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),e.chars&&e.chars(r(g)))}if(a==l)throw L("badparse",a);l=a}c()}function r(a){if(!a)return"";var e=M.exec(a);a=e[1];var d=e[3];if(e=e[2])n.innerHTML=e.replace(/</g,"<"),e="textContent"in n?n.textContent:n.innerText;return a+e+d}function B(a){return a.replace(/&/g,
|
||||
"&").replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function s(a,e){var d=!1,c=h.bind(a,a.push);return{start:function(a,g,f){a=h.lowercase(a);!d&&x[a]&&(d=a);d||!0!==C[a]||(c("<"),c(a),h.forEach(g,function(d,f){var g=h.lowercase(f),k="img"===a&&"src"===g||"background"===g;!0!==O[g]||!0===D[g]&&!e(d,k)||(c(" "),c(f),c('="'),c(B(d)),c('"'))}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==C[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||
|
||||
c(B(a))}}}var L=h.$$minErr("$sanitize"),A=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,z=/^<\s*\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^</,J=/^<\s*\//,H=/\x3c!--(.*?)--\x3e/g,y=/<!DOCTYPE([^>]*?)>/i,I=/<!\[CDATA\[(.*?)]]\x3e/g,N=/([^\#-~| |!])/g,w=k("area,br,col,hr,img,wbr");p=k("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");q=k("rp,rt");var v=h.extend({},q,p),t=h.extend({},p,k("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),
|
||||
u=h.extend({},q,k("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),x=k("script,style"),C=h.extend({},w,t,u,v),D=k("background,cite,href,longdesc,src,usemap"),O=h.extend({},D,k("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,target,title,type,valign,value,vspace,width")),
|
||||
n=document.createElement("pre"),M=/^(\s*)([\s\S]*?)(\s*)$/;h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(e){var d=[];F(e,s(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var e=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,d=/^mailto:/;return function(c,b){function g(a){a&&m.push(E(a))}function f(a,c){m.push("<a ");h.isDefined(b)&&
|
||||
(m.push('target="'),m.push(b),m.push('" '));m.push('href="');m.push(a);m.push('">');g(c);m.push("</a>")}if(!c)return c;for(var l,k=c,m=[],n,p;l=k.match(e);)n=l[0],l[2]==l[3]&&(n="mailto:"+n),p=l.index,g(k.substr(0,p)),f(n,l[0].replace(d,"")),k=k.substring(p+l[0].length);g(k);return a(m.join(""))}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-sanitize.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "angular-sanitize",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular-sanitize.js",
|
||||
"dependencies": {
|
||||
"angular": "1.2.16"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "angular-ui-router",
|
||||
"version": "0.2.7",
|
||||
"main": "./release/angular-ui-router.js",
|
||||
"dependencies": {
|
||||
"angular": ">= 1.1.5"
|
||||
},
|
||||
"homepage": "https://github.com/angular-ui/ui-router",
|
||||
"_release": "0.2.7",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "0.2.7",
|
||||
"commit": "20fcbf8384090a092c7c91a26443e3c818438120"
|
||||
},
|
||||
"_source": "git://github.com/angular-ui/ui-router.git",
|
||||
"_target": "0.2.7",
|
||||
"_originalSource": "angular-ui-router"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# master only
|
||||
build
|
||||
|
||||
# common
|
||||
node_modules
|
||||
bower_components
|
||||
.DS_Store
|
||||
*~
|
||||
|
||||
# webstorm files
|
||||
.idea
|
||||
@@ -0,0 +1,13 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install -g karma
|
||||
- npm install -g grunt-cli
|
||||
- ./nodeserver.sh > /dev/null &
|
||||
|
||||
script:
|
||||
- grunt
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*global module:false*/
|
||||
module.exports = function (grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
builddir: 'build',
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
buildtag: '-dev-' + grunt.template.today('yyyy-mm-dd'),
|
||||
meta: {
|
||||
banner: '/**\n' +
|
||||
' * <%= pkg.description %>\n' +
|
||||
' * @version v<%= pkg.version %><%= buildtag %>\n' +
|
||||
' * @link <%= pkg.homepage %>\n' +
|
||||
' * @license MIT License, http://www.opensource.org/licenses/MIT\n' +
|
||||
' */'
|
||||
},
|
||||
clean: [ '<%= builddir %>' ],
|
||||
concat: {
|
||||
options: {
|
||||
banner: '<%= meta.banner %>\n\n'+
|
||||
'/* commonjs package manager support (eg componentjs) */\n'+
|
||||
'if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){\n'+
|
||||
' module.exports = \'ui.router\';\n'+
|
||||
'}\n\n'+
|
||||
'(function (window, angular, undefined) {\n',
|
||||
footer: '})(window, window.angular);'
|
||||
},
|
||||
build: {
|
||||
src: [
|
||||
'src/common.js',
|
||||
'src/resolve.js',
|
||||
'src/templateFactory.js',
|
||||
'src/urlMatcherFactory.js',
|
||||
'src/urlRouter.js',
|
||||
'src/state.js',
|
||||
'src/view.js',
|
||||
'src/viewDirective.js',
|
||||
'src/stateDirectives.js',
|
||||
'src/compat.js'
|
||||
],
|
||||
dest: '<%= builddir %>/<%= pkg.name %>.js'
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
banner: '<%= meta.banner %>\n'
|
||||
},
|
||||
build: {
|
||||
files: {
|
||||
'<%= builddir %>/<%= pkg.name %>.min.js': ['<banner:meta.banner>', '<%= concat.build.dest %>']
|
||||
}
|
||||
}
|
||||
},
|
||||
release: {
|
||||
files: ['<%= pkg.name %>.js', '<%= pkg.name %>.min.js'],
|
||||
src: '<%= builddir %>',
|
||||
dest: 'release'
|
||||
},
|
||||
jshint: {
|
||||
all: ['Gruntfile.js', 'src/*.js', '<%= builddir %>/<%= pkg.name %>.js'],
|
||||
options: {
|
||||
eqnull: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['src/*.js', 'test/**/*.js'],
|
||||
tasks: ['build', 'karma:background:run']
|
||||
},
|
||||
connect: {
|
||||
server: {},
|
||||
sample: {
|
||||
options:{
|
||||
port: 5555,
|
||||
keepalive: true
|
||||
}
|
||||
}
|
||||
},
|
||||
karma: {
|
||||
options: {
|
||||
configFile: 'config/karma.js'
|
||||
},
|
||||
unit: {
|
||||
singleRun: true
|
||||
},
|
||||
background: {
|
||||
background: true,
|
||||
browsers: [ grunt.option('browser') || 'PhantomJS' ]
|
||||
}
|
||||
},
|
||||
changelog: {
|
||||
options: {
|
||||
dest: 'CHANGELOG.md'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('default', ['build', 'jshint', 'karma:unit']);
|
||||
grunt.registerTask('build', 'Perform a normal build', ['concat', 'uglify']);
|
||||
grunt.registerTask('dist', 'Perform a clean build and generate documentation', ['clean', 'build', 'jsdoc']);
|
||||
grunt.registerTask('release', 'Tag and perform a release', ['prepare-release', 'dist', 'perform-release']);
|
||||
grunt.registerTask('dev', 'Run dev server and watch for changes', ['build', 'connect:server', 'karma:background', 'watch']);
|
||||
grunt.registerTask('sample', 'Run connect server with keepalive:true for sample app development', ['connect:sample']);
|
||||
|
||||
grunt.registerTask('jsdoc', 'Generate documentation', function () {
|
||||
promising(this,
|
||||
system('node_modules/jsdoc/jsdoc -c config/jsdoc.js -d \'' + grunt.config('builddir') + '\'/doc src')
|
||||
);
|
||||
});
|
||||
|
||||
grunt.registerTask('publish-pages', 'Publish a clean build, docs, and sample to github.io', function () {
|
||||
promising(this,
|
||||
ensureCleanMaster().then(function () {
|
||||
shjs.rm('-rf', 'build');
|
||||
return system('git checkout gh-pages');
|
||||
}).then(function () {
|
||||
return system('git merge master');
|
||||
}).then(function () {
|
||||
return system('grunt dist');
|
||||
}).then(function () {
|
||||
return system('git commit -a -m \'Automatic gh-pages build\'');
|
||||
}).then(function () {
|
||||
return system('git checkout master');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
grunt.registerTask('prepare-release', function () {
|
||||
var bower = grunt.file.readJSON('bower.json'),
|
||||
component = grunt.file.readJSON('component.json'),
|
||||
version = bower.version;
|
||||
if (version != grunt.config('pkg.version')) throw 'Version mismatch in bower.json';
|
||||
if (version != component.version) throw 'Version mismatch in component.json';
|
||||
|
||||
promising(this,
|
||||
ensureCleanMaster().then(function () {
|
||||
return exec('git tag -l \'' + version + '\'');
|
||||
}).then(function (result) {
|
||||
if (result.stdout.trim() !== '') throw 'Tag \'' + version + '\' already exists';
|
||||
grunt.config('buildtag', '');
|
||||
grunt.config('builddir', 'release');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
grunt.registerTask('perform-release', function () {
|
||||
grunt.task.requires([ 'prepare-release', 'dist' ]);
|
||||
|
||||
var version = grunt.config('pkg.version'), releasedir = grunt.config('builddir');
|
||||
promising(this,
|
||||
system('git add \'' + releasedir + '\'').then(function () {
|
||||
return system('git commit -m \'release ' + version + '\'');
|
||||
}).then(function () {
|
||||
return system('git tag \'' + version + '\'');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
// Helpers for custom tasks, mainly around promises / exec
|
||||
var exec = require('faithful-exec'), shjs = require('shelljs');
|
||||
|
||||
function system(cmd) {
|
||||
grunt.log.write('% ' + cmd + '\n');
|
||||
return exec(cmd).then(function (result) {
|
||||
grunt.log.write(result.stderr + result.stdout);
|
||||
}, function (error) {
|
||||
grunt.log.write(error.stderr + '\n');
|
||||
throw 'Failed to run \'' + cmd + '\'';
|
||||
});
|
||||
}
|
||||
|
||||
function promising(task, promise) {
|
||||
var done = task.async();
|
||||
promise.then(function () {
|
||||
done();
|
||||
}, function (error) {
|
||||
grunt.log.write(error + '\n');
|
||||
done(false);
|
||||
});
|
||||
}
|
||||
|
||||
function ensureCleanMaster() {
|
||||
return exec('git symbolic-ref HEAD').then(function (result) {
|
||||
if (result.stdout.trim() !== 'refs/heads/master') throw 'Not on master branch, aborting';
|
||||
return exec('git status --porcelain');
|
||||
}).then(function (result) {
|
||||
if (result.stdout.trim() !== '') throw 'Working copy is dirty, aborting';
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2013 Karsten Sperling
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,280 @@
|
||||
# AngularUI Router [](https://travis-ci.org/angular-ui/ui-router)
|
||||
|
||||
#### The de-facto solution to flexible routing with nested views
|
||||
---
|
||||
**[Download 0.2.7](http://angular-ui.github.io/ui-router/release/angular-ui-router.js)** (or **[Minified](http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js)**) **|**
|
||||
**[Learn](#resources) |**
|
||||
**[Discuss](https://groups.google.com/forum/#!categories/angular-ui/router) |**
|
||||
**[Get Help](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) |**
|
||||
**[Report an Issue](#report-an-issue) |**
|
||||
**[Contribute](#contribute)**
|
||||
|
||||
---
|
||||
|
||||
AngularUI Router is a routing framework for [AngularJS](http://angularjs.org), which allows you to organize the
|
||||
parts of your interface into a [*state machine*](https://en.wikipedia.org/wiki/Finite-state_machine). Unlike the
|
||||
[`$route` service](http://docs.angularjs.org/api/ngRoute.$route) in Angular core, which is organized around URL
|
||||
routes, UI-Router is organized around [*states*](https://github.com/angular-ui/ui-router/blob/master/sample/states.js#L28-L269),
|
||||
which may optionally have routes, as well as other behavior, attached.
|
||||
|
||||
States are bound to *named*, *nested* and *parallel views*, allowing you to powerfully manage your application's interface.
|
||||
|
||||
-
|
||||
**Warning:** *UI-Router is pre-beta and under active development. As such, while this library is well-tested, the API is subject
|
||||
to change. Using it in a project that requires guaranteed stability is not recommended.*
|
||||
|
||||
|
||||
## Get Started
|
||||
|
||||
**(1)** Get UI-Router in one of 4 ways:
|
||||
- clone & [build](#developing) this repository
|
||||
- [download the release](http://angular-ui.github.io/ui-router/release/angular-ui-router.js) (or [minified](http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js))
|
||||
- via **[Bower](http://bower.io/)**: by running `$ bower install angular-ui-router` from your console
|
||||
- or via **[Component](https://github.com/component/component)**: by running `$ component install angular-ui/ui-router` from your console
|
||||
|
||||
**(2)** Include `angular-ui-router.js` (or `angular-ui-router.min.js`) in your `index.html`, after including Angular itself (For Component users: ignore this step)
|
||||
|
||||
**(3)** Add `'ui.router'` to your main module's list of dependencies (For Component users: replace `'ui.router'` with `require('angular-ui-router')`)
|
||||
|
||||
When you're done, your setup should look similar to the following:
|
||||
|
||||
>
|
||||
```html
|
||||
<!doctype html>
|
||||
<html ng-app="myApp">
|
||||
<head>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
|
||||
<script src="js/angular-ui-router.min.js"></script>
|
||||
<script>
|
||||
var myApp = angular.module('myApp', ['ui.router']);
|
||||
// For Component users, it should look like this:
|
||||
// var myApp = angular.module('myApp', [require('angular-ui-router')]);
|
||||
</script>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Nested States & Views
|
||||
|
||||
The majority of UI-Router's power is in its ability to nest states & views.
|
||||
|
||||
**(1)** First, follow the [setup](#get-started) instructions detailed above.
|
||||
|
||||
**(2)** Then, add a [`ui-view` directive](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-view) to the `<body />` of your app.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<body>
|
||||
<div ui-view></div>
|
||||
<!-- We'll also add some navigation: -->
|
||||
<a ui-sref="state1">State 1</a>
|
||||
<a ui-sref="state2">State 2</a>
|
||||
</body>
|
||||
```
|
||||
|
||||
**(3)** You'll notice we also added some links with [`ui-sref` directives](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref). In addition to managing state transitions, this directive auto-generates the `href` attribute of the `<a />` element it's attached to, if the corresponding state has a URL. Next we'll add some templates. These will plug into the `ui-view` within `index.html`. Notice that they have their own `ui-view` as well! That is the key to nesting states and views.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state1.html -->
|
||||
<h1>State 1</h1>
|
||||
<hr/>
|
||||
<a ui-sref="state1.list">Show List</a>
|
||||
<div ui-view></div>
|
||||
```
|
||||
```html
|
||||
<!-- partials/state2.html -->
|
||||
<h1>State 2</h1>
|
||||
<hr/>
|
||||
<a ui-sref="state2.list">Show List</a>
|
||||
<div ui-view></div>
|
||||
```
|
||||
|
||||
**(4)** Next, we'll add some child templates. *These* will get plugged into the `ui-view` of their parent state templates.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state1.list.html -->
|
||||
<h3>List of State 1 Items</h3>
|
||||
<ul>
|
||||
<li ng-repeat="item in items">{{ item }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state2.list.html -->
|
||||
<h3>List of State 2 Things</h3>
|
||||
<ul>
|
||||
<li ng-repeat="thing in things">{{ thing }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**(5)** Finally, we'll wire it all up with `$stateProvider`. Set up your states in the module config, as in the following:
|
||||
|
||||
|
||||
>
|
||||
```javascript
|
||||
myApp.config(function($stateProvider, $urlRouterProvider) {
|
||||
//
|
||||
// For any unmatched url, redirect to /state1
|
||||
$urlRouterProvider.otherwise("/state1");
|
||||
//
|
||||
// Now set up the states
|
||||
$stateProvider
|
||||
.state('state1', {
|
||||
url: "/state1",
|
||||
templateUrl: "partials/state1.html"
|
||||
})
|
||||
.state('state1.list', {
|
||||
url: "/list",
|
||||
templateUrl: "partials/state1.list.html",
|
||||
controller: function($scope) {
|
||||
$scope.items = ["A", "List", "Of", "Items"];
|
||||
}
|
||||
})
|
||||
.state('state2', {
|
||||
url: "/state2",
|
||||
templateUrl: "partials/state2.html"
|
||||
})
|
||||
.state('state2.list', {
|
||||
url: "/list",
|
||||
templateUrl: "partials/state2.list.html",
|
||||
controller: function($scope) {
|
||||
$scope.things = ["A", "Set", "Of", "Things"];
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**(6)** See this quick start example in action.
|
||||
>**[Go to Quick Start Plunker for Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview)**
|
||||
|
||||
**(7)** This only scratches the surface
|
||||
>**[Dive Deeper!](https://github.com/angular-ui/ui-router/wiki)**
|
||||
|
||||
|
||||
### Multiple & Named Views
|
||||
|
||||
Another great feature is the ability to have multiple `ui-view`s view per template.
|
||||
|
||||
**Pro Tip:** *While multiple parallel views are a powerful feature, you'll often be able to manage your
|
||||
interfaces more effectively by nesting your views, and pairing those views with nested states.*
|
||||
|
||||
**(1)** Follow the [setup](#get-started) instructions detailed above.
|
||||
|
||||
**(2)** Add one or more `ui-view` to your app, give them names.
|
||||
>
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<body>
|
||||
<div ui-view="viewA"></div>
|
||||
<div ui-view="viewB"></div>
|
||||
<!-- Also a way to navigate -->
|
||||
<a ui-sref="route1">Route 1</a>
|
||||
<a ui-sref="route2">Route 2</a>
|
||||
</body>
|
||||
```
|
||||
|
||||
**(3)** Set up your states in the module config:
|
||||
>
|
||||
```javascript
|
||||
myApp.config(function($stateProvider, $routeProvider){
|
||||
$stateProvider
|
||||
.state('index', {
|
||||
url: "",
|
||||
views: {
|
||||
"viewA": { template: "index.viewA" },
|
||||
"viewB": { template: "index.viewB" }
|
||||
}
|
||||
})
|
||||
.state('route1', {
|
||||
url: "/route1",
|
||||
views: {
|
||||
"viewA": { template: "route1.viewA" },
|
||||
"viewB": { template: "route1.viewB" }
|
||||
}
|
||||
})
|
||||
.state('route2', {
|
||||
url: "/route2",
|
||||
views: {
|
||||
"viewA": { template: "route2.viewA" },
|
||||
"viewB": { template: "route2.viewB" }
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**(4)** See this quick start example in action.
|
||||
>**[Go to Quick Start Plunker for Multiple & Named Views](http://plnkr.co/edit/SDOcGS?p=preview)**
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
* [In-Depth Guide](https://github.com/angular-ui/ui-router/wiki)
|
||||
* [API Quick Reference](https://github.com/angular-ui/ui-router/wiki/Quick-Reference)
|
||||
* [Sample App](http://angular-ui.github.com/ui-router/sample/) ([Source](https://github.com/angular-ui/ui-router/tree/master/sample))
|
||||
* [FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions)
|
||||
* [Introduction Video](http://egghead.io/lessons/angularjs-introduction-ui-router)
|
||||
* [Slides from CincyNg Meetup](http://slid.es/timkindberg/ui-router#/)
|
||||
|
||||
## Report an Issue
|
||||
|
||||
Help us make UI-Router better! If you think you might have found a bug, or some other weirdness, start by making sure
|
||||
it hasn't already been reported. You can [search through existing issues](https://github.com/angular-ui/ui-router/search?q=wat%3F&type=Issues)
|
||||
to see if someone's reported one similar to yours.
|
||||
|
||||
If not, then [create a plunkr](http://plnkr.co/edit/u18KQc?p=preview) that demonstrates the problem (try to use as little code
|
||||
as possible: the more minimalist, the faster we can debug it).
|
||||
|
||||
Next, [create a new issue](https://github.com/angular-ui/ui-router/issues/new) that briefly explains the problem,
|
||||
and provides a bit of background as to the circumstances that triggered it. Don't forget to include the link to
|
||||
that plunkr you created!
|
||||
|
||||
**Note**: If you're unsure how a feature is used, or are encountering some unexpected behavior that you aren't sure
|
||||
is a bug, it's best to talk it out in the
|
||||
[Google Group](https://groups.google.com/forum/#!categories/angular-ui/router) or on
|
||||
[StackOverflow](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) before reporting it. This
|
||||
keeps development streamlined, and helps us focus on building great software.
|
||||
|
||||
Please keep in mind that the issue tracker is for *issues*. Please do *not* post an issue if you need help or support. Instead, see one of the above-mentioned forums or [IRC](irc://irc.freenode.net/#angularjs).
|
||||
|
||||
|
||||
## Contribute
|
||||
|
||||
**(1)** See the **[Developing](#developing)** section below, to get the development version of UI-Router up and running on your local machine.
|
||||
|
||||
**(2)** Check out the [roadmap](https://github.com/angular-ui/ui-router/issues/milestones) to see where the project is headed, and if your feature idea fits with where we're headed.
|
||||
|
||||
**(3)** If you're not sure, [open an RFC](https://github.com/angular-ui/ui-router/issues/new?title=RFC:%20My%20idea) to get some feedback on your idea.
|
||||
|
||||
**(4)** Finally, commit some code and open a pull request. Code & commits should abide by the following rules:
|
||||
|
||||
- *Always* have test coverage for new features (or regression tests for bug fixes), and *never* break existing tests
|
||||
- Commits should represent one logical change each; if a feature goes through multiple iterations, squash your commits down to one
|
||||
- Make sure to follow the [Angular commit message format](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format) so your change will appear in the changelog of the next release.
|
||||
- Changes should always respect the coding style of the project
|
||||
|
||||
|
||||
|
||||
## Developing
|
||||
|
||||
UI-Router uses <code>grunt >= 0.4.x</code>. Make sure to upgrade your environment and read the
|
||||
[Migration Guide](http://gruntjs.com/upgrading-from-0.3-to-0.4).
|
||||
|
||||
Dependencies for building from source and running tests:
|
||||
|
||||
* [grunt-cli](https://github.com/gruntjs/grunt-cli) - run: `$ npm install -g grunt-cli`
|
||||
* Then, install the development dependencies by running `$ npm install` from the project directory
|
||||
|
||||
There are a number of targets in the gruntfile that are used to generating different builds:
|
||||
|
||||
* `grunt`: Perform a normal build, runs jshint and karma tests
|
||||
* `grunt build`: Perform a normal build
|
||||
* `grunt dist`: Perform a clean build and generate documentation
|
||||
* `grunt dev`: Run dev server (sample app) and watch for changes, builds and runs karma tests on changes.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "angular-ui-router",
|
||||
"version": "0.2.7",
|
||||
"main": "./release/angular-ui-router.js",
|
||||
"dependencies": {
|
||||
"angular": ">= 1.1.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "angular-ui-router",
|
||||
"version": "0.2.7",
|
||||
"description": "State-based routing for AngularJS",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"states",
|
||||
"routing"
|
||||
],
|
||||
"main": "release/angular-ui-router.min.js",
|
||||
"scripts": [
|
||||
"release/angular-ui-router.min.js"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repo": "angular-ui/ui-router"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [ "plugins/markdown" ],
|
||||
"markdown": {
|
||||
"parser": "gfm"
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// Testacular configuration file
|
||||
module.exports = function (karma) {
|
||||
karma.set({
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath: '..',
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'lib/angular-1.1.5.js',
|
||||
'test/lib/angular-mocks-1.1.5.js',
|
||||
'test/testUtils.js',
|
||||
|
||||
'src/common.js',
|
||||
'src/resolve.js',
|
||||
'src/templateFactory.js',
|
||||
'src/urlMatcherFactory.js',
|
||||
'src/urlRouter.js',
|
||||
'src/view.js',
|
||||
'src/state.js',
|
||||
'src/viewDirective.js',
|
||||
'src/stateDirectives.js',
|
||||
'src/stateFilters.js',
|
||||
'src/compat.js',
|
||||
|
||||
'test/*Spec.js',
|
||||
// 'test/compat/matchers.js',
|
||||
// 'test/compat/*Spec.js',
|
||||
],
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
// use dots reporter, as travis terminal does not support escaping sequences
|
||||
// possible values: 'dots' || 'progress'
|
||||
reporters: 'dots',
|
||||
|
||||
// these are default values, just to show available options
|
||||
|
||||
// web server port
|
||||
port: 8080,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
|
||||
logLevel: karma.LOG_DEBUG,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// polling interval in ms (ignored on OS that support inotify)
|
||||
autoWatchInterval: 0,
|
||||
|
||||
// Start these browsers, currently available:
|
||||
// - Chrome
|
||||
// - ChromeCanary
|
||||
// - Firefox
|
||||
// - Opera
|
||||
// - Safari
|
||||
// - PhantomJS
|
||||
browsers: [ 'PhantomJS' ]
|
||||
})
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "angular-ui-router",
|
||||
"description": "State-based routing for AngularJS",
|
||||
"version": "0.2.7",
|
||||
"homepage": "http://angular-ui.github.com/",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Nate Abele",
|
||||
"email": "nate@unionofrad.com",
|
||||
"web": "https://github.com/nateabele"
|
||||
},
|
||||
{
|
||||
"name": "Tim Kindberg",
|
||||
"web": "https://github.com/timkindberg"
|
||||
},
|
||||
{
|
||||
"name": "Karsten Sperling",
|
||||
"web": "https://github.com/ksperling"
|
||||
}
|
||||
],
|
||||
"maintainers": [{
|
||||
"name": "AngularUI",
|
||||
"web": "https://github.com/angular-ui?tab=members"
|
||||
}],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular-ui/ui-router.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular-ui/ui-router/issues"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/angular-ui/ui-router/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-uglify": "~0.2.7",
|
||||
"grunt-contrib-jshint": "~0.7.1",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-karma": "~0.6.2",
|
||||
"jsdoc": "git://github.com/jsdoc3/jsdoc.git#v3.2.2",
|
||||
"shelljs": "~0.2.6",
|
||||
"faithful-exec": "~0.1.0",
|
||||
"karma-firefox-launcher": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.0",
|
||||
"karma-html2js-preprocessor": "~0.1.0",
|
||||
"karma-jasmine": "~0.1.3",
|
||||
"karma-requirejs": "~0.2.0",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"karma-coffee-preprocessor": "~0.1.0",
|
||||
"karma": "~0.10.4",
|
||||
"karma-phantomjs-launcher": "~0.1.0",
|
||||
"load-grunt-tasks": "~0.2.0",
|
||||
"grunt-conventional-changelog": "~1.0.0"
|
||||
}
|
||||
}
|
||||
+1769
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,555 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Module: $resolve</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Module: $resolve</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
<h2>
|
||||
$resolve
|
||||
</h2>
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description"><p>Service (<code>ui-util</code>). Manages resolution of (acyclic) graphs of promises.</p></div>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="resolve.js.html">resolve.js</a>, <a href="resolve.js.html#line1">line 1</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Requires</h3>
|
||||
|
||||
<ul>
|
||||
<li>module:$q</li>
|
||||
|
||||
<li>module:$injector</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Methods</h3>
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="resolve"><span class="type-signature"></span>resolve<span class="signature">(invocables, <span class="optional">locals</span>, <span class="optional">parent</span>, <span class="optional">self</span>)</span><span class="type-signature"> → {Promise.<Object>}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Resolves a set of invocables. An invocable is a function to be invoked via <code>$injector.invoke()</code>,
|
||||
and can have an arbitrary number of dependencies. An invocable can either return a value directly,
|
||||
or a <code>$q</code> 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)</p>
|
||||
<ul>
|
||||
<li>from the specified <code>locals</code></li>
|
||||
<li>from another invocable that is part of this <code>$resolve</code> call</li>
|
||||
<li>from an invocable that is inherited from a <code>parent</code> call to <code>$resolve</code> (or recursively
|
||||
from any ancestor <code>$resolve</code> of that parent).</li>
|
||||
</ul>
|
||||
<p>The return value of <code>$resolve</code> is a promise for an object that contains (in this order of precedence)</p>
|
||||
<ul>
|
||||
<li>any <code>locals</code> (if specified)</li>
|
||||
<li>the resolved return values of all injectables</li>
|
||||
<li>any values inherited from a <code>parent</code> call to <code>$resolve</code> (if specified)</li>
|
||||
</ul>
|
||||
<p>The promise will resolve after the <code>parent</code> promise (if any) and all promises returned by injectables
|
||||
have been resolved. If any invocable (or <code>$injector.invoke</code>) throws an exception, or if a promise
|
||||
returned by an invocable is rejected, the <code>$resolve</code> promise is immediately rejected with the same error.
|
||||
A rejection of a <code>parent</code> promise (if specified) will likewise be propagated immediately. Once the
|
||||
<code>$resolve</code> promise has been rejected, no further invocables will be called.</p>
|
||||
<p>Cyclic dependencies between invocables are not permitted and will caues <code>$resolve</code> 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 <code>parent</code> injectable of the same name. This allows inherited values
|
||||
to be decorated. Note that in this case any other injectable in the same <code>$resolve</code> with the same
|
||||
dependency would see the decorated value, not the inherited value.</p>
|
||||
<p>Note that missing dependencies -- unlike cyclic dependencies -- will cause an (asynchronous) rejection
|
||||
of the <code>$resolve</code> promise rather than a (synchronous) exception.</p>
|
||||
<p>Invocables are invoked eagerly as soon as all dependencies are available. This is true even for
|
||||
dependencies inherited from a <code>parent</code> call to <code>$resolve</code>.</p>
|
||||
<p>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 <code>$injector.get()</code>. This is supported primarily for backwards-compatibility with the
|
||||
<code>resolve</code> property of <code>$routeProvider</code> routes.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
<th>Argument</th>
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>invocables</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object.<string, (function()|string)></span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
<td class="attributes">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>functions to invoke or <code>$injector</code> services to fetch.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>locals</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object.<string, *></span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
<td class="attributes">
|
||||
|
||||
<optional><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>values to make available to the injectables</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>parent</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Promise.<Object></span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
<td class="attributes">
|
||||
|
||||
<optional><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>a promise returned by another call to <code>$resolve</code>.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>self</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
<td class="attributes">
|
||||
|
||||
<optional><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>the <code>this</code> for the invoked methods</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="resolve.js.html">resolve.js</a>, <a href="resolve.js.html#line209">line 209</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>Promise for an object that contains the resolved return value
|
||||
of all invocables, as well as any inherited and local values.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">Promise.<Object></span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="study"><span class="type-signature"></span>study<span class="signature">(invocables)</span><span class="type-signature"> → {function}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Studies a set of invocables that are likely to be used multiple times.
|
||||
$resolve.study(invocables)(locals, parent, self)
|
||||
is equivalent to
|
||||
$resolve.resolve(invocables, locals, parent, self)
|
||||
but the former is more efficient (in fact <code>resolve</code> just calls <code>study</code> internally).
|
||||
See module:$resolve/resolve for details.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>invocables</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="resolve.js.html">resolve.js</a>, <a href="resolve.js.html#line29">line 29</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">function</span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Class: $urlMatcherFactory</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Class: $urlMatcherFactory</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
<h2>
|
||||
$urlMatcherFactory
|
||||
</h2>
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="$urlMatcherFactory"><span class="type-signature"></span>new $urlMatcherFactory<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Service. Factory for <a href="UrlMatcher.html">UrlMatcher</a> instances. The factory is also available to providers
|
||||
under the name <code>$urlMatcherFactoryProvider</code>.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line219">line 219</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Methods</h3>
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="compile"><span class="type-signature"></span>compile<span class="signature">(pattern)</span><span class="type-signature"> → {<a href="UrlMatcher.html">UrlMatcher</a>}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Creates a <a href="UrlMatcher.html">UrlMatcher</a> for the specified pattern.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>pattern</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>The URL pattern.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line226">line 226</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>The UrlMatcher.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type"><a href="UrlMatcher.html">UrlMatcher</a></span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="isMatcher"><span class="type-signature"></span>isMatcher<span class="signature">(o)</span><span class="type-signature"> → {boolean}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Returns true if the specified object is a UrlMatcher, or false otherwise.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>o</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line238">line 238</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">boolean</span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,838 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Class: UrlMatcher</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Class: UrlMatcher</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
<h2>
|
||||
UrlMatcher
|
||||
</h2>
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="UrlMatcher"><span class="type-signature"></span>new UrlMatcher<span class="signature">(pattern)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Matches URLs against patterns and extracts named parameters from the path or the search
|
||||
part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
|
||||
of search parameters. Multiple search parameter names are separated by '&'. Search parameters
|
||||
do not influence whether or not a URL is matched, but their values are passed through into
|
||||
the matched parameters returned by <a href="UrlMatcher.html#exec">exec</a>.</p>
|
||||
<p>Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
|
||||
syntax, which optionally allows a regular expression for the parameter to be specified:</p>
|
||||
<ul>
|
||||
<li>':' name - colon placeholder</li>
|
||||
<li>'*' name - catch-all placeholder</li>
|
||||
<li>'{' name '}' - curly placeholder</li>
|
||||
<li>'{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain
|
||||
curly braces, they must be in matched pairs or escaped with a backslash.</li>
|
||||
</ul>
|
||||
<p>Parameter names may contain only word characters (latin letters, digits, and underscore) and
|
||||
must be unique within the pattern (across both path and search parameters). For colon
|
||||
placeholders or curly placeholders without an explicit regexp, a path parameter matches any
|
||||
number of characters other than '/'. For catch-all placeholders the path parameter matches
|
||||
any number of characters.</p>
|
||||
<h3>Examples</h3>
|
||||
<ul>
|
||||
<li>'/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for
|
||||
trailing slashes, and patterns have to match the entire path, not just a prefix.</li>
|
||||
<li>'/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
|
||||
'/user/bob/details'. The second path segment will be captured as the parameter 'id'.</li>
|
||||
<li>'/user/{id}' - Same as the previous example, but using curly brace syntax.</li>
|
||||
<li>'/user/{id:[^/]*}' - Same as the previous example.</li>
|
||||
<li>'/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id
|
||||
parameter consists of 1 to 8 hex digits.</li>
|
||||
<li>'/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the
|
||||
path into the parameter 'path'.</li>
|
||||
<li>'/files/*path' - ditto.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>pattern</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>the pattern to compile into a matcher.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
<h5 class="subsection-title">Properties:</h5>
|
||||
|
||||
<dl>
|
||||
|
||||
<table class="props">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>prefix</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>A static prefix of this pattern. The matcher guarantees that any
|
||||
URL matching this matcher (i.e. any string for which <a href="UrlMatcher.html#exec">exec()</a> returns
|
||||
non-null) will start with this prefix.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table></dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line44">line 44</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Methods</h3>
|
||||
|
||||
<dl>
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="concat"><span class="type-signature"></span>concat<span class="signature">(pattern)</span><span class="type-signature"> → {<a href="UrlMatcher.html">UrlMatcher</a>}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Returns a new matcher for a pattern constructed by appending the path part and adding the
|
||||
search parameters of the specified pattern to this pattern. The current pattern is not
|
||||
modified. This can be understood as creating a pattern for URLs that are relative to (or
|
||||
suffixes of) the current pattern.</p>
|
||||
<h3>Example</h3>
|
||||
<p>The following two matchers are equivalent:</p>
|
||||
<pre><code>new UrlMatcher('/user/{id}?q').concat('/details?date');
|
||||
new UrlMatcher('/user/{id}/details?q&date');</code></pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>pattern</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>The pattern to append.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line128">line 128</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>A matcher for the concatenated pattern.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type"><a href="UrlMatcher.html">UrlMatcher</a></span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="exec"><span class="type-signature"></span>exec<span class="signature">(path, searchParams)</span><span class="type-signature"> → {Object}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Tests the specified path against this matcher, and returns an object containing the captured
|
||||
parameter values, or null if the path does not match. The returned object contains the values
|
||||
of any search parameters that are mentioned in the pattern, but their value may be null if
|
||||
they are not present in <code>searchParams</code>. This means that search parameters are always treated
|
||||
as optional.</p>
|
||||
<h3>Example</h3>
|
||||
<pre><code>new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
|
||||
// returns { id:'bob', q:'hello', r:null }</code></pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>path</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>The URL path to match, e.g. <code>$location.path()</code>.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>searchParams</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>URL search parameters, e.g. <code>$location.search()</code>.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line156">line 156</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>The captured parameter values.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="format"><span class="type-signature"></span>format<span class="signature">(values)</span><span class="type-signature"> → {string}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Creates a URL that matches this pattern by substituting the specified values
|
||||
for the path and search parameters. Null values for path parameters are
|
||||
treated as empty strings.</p>
|
||||
<h3>Example</h3>
|
||||
<pre><code>new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
|
||||
// returns '/user/bob?q=yes'</code></pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>values</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Object</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last"><p>the values to substitute for the parameters in this pattern.</p></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line195">line 195</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>the formatted URL (path and optionally search part).</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">string</span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
|
||||
<dt>
|
||||
<h4 class="name" id="parameters"><span class="type-signature"></span>parameters<span class="signature">()</span><span class="type-signature"> → {Array.<string>}</span></h4>
|
||||
|
||||
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
<div class="description">
|
||||
<p>Returns the names of all path and search parameters of this pattern in an unspecified order.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="urlMatcherFactory.js.html">urlMatcherFactory.js</a>, <a href="urlMatcherFactory.js.html#line177">line 177</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Returns:</h5>
|
||||
|
||||
|
||||
<div class="param-desc">
|
||||
<p>An array of parameter names. Must be treated as read-only. If the
|
||||
pattern has no parameters, an empty array is returned.</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>
|
||||
Type
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<span class="param-type">Array.<string></span>
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,222 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: common.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: common.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source"><code>/*jshint globalstrict:true*/
|
||||
/*global angular:false*/
|
||||
'use strict';
|
||||
|
||||
var isDefined = angular.isDefined,
|
||||
isFunction = angular.isFunction,
|
||||
isString = angular.isString,
|
||||
isObject = angular.isObject,
|
||||
isArray = angular.isArray,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy;
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, { prototype: parent }))(), extra);
|
||||
}
|
||||
|
||||
function merge(dst) {
|
||||
forEach(arguments, function(obj) {
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key) {
|
||||
if (!dst.hasOwnProperty(key)) dst[key] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the common ancestor path between two states.
|
||||
*
|
||||
* @param {Object} first The first state.
|
||||
* @param {Object} second The second state.
|
||||
* @return {Array} Returns an array of state names in descending order, not including the root.
|
||||
*/
|
||||
function ancestors(first, second) {
|
||||
var path = [];
|
||||
|
||||
for (var n in first.path) {
|
||||
if (first.path[n] === "") continue;
|
||||
if (!second.path[n]) break;
|
||||
path.push(first.path[n]);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Object.keys()`.
|
||||
*
|
||||
* @param {Object} object A JavaScript object.
|
||||
* @return {Array} Returns the keys of the object as an array.
|
||||
*/
|
||||
function keys(object) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(object);
|
||||
}
|
||||
var result = [];
|
||||
|
||||
angular.forEach(object, function(val, key) {
|
||||
result.push(key);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Array.prototype.indexOf()`.
|
||||
*
|
||||
* @param {Array} array A JavaScript array.
|
||||
* @param {*} value A value to search the array for.
|
||||
* @return {Number} Returns the array index value of `value`, or `-1` if not present.
|
||||
*/
|
||||
function arraySearch(array, value) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return array.indexOf(value, Number(arguments[2]) || 0);
|
||||
}
|
||||
var len = array.length >>> 0, from = Number(arguments[2]) || 0;
|
||||
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
|
||||
|
||||
if (from < 0) from += len;
|
||||
|
||||
for (; from < len; from++) {
|
||||
if (from in array && array[from] === value) return from;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a set of parameters with all parameters inherited between the common parents of the
|
||||
* current state and a given destination state.
|
||||
*
|
||||
* @param {Object} currentParams The value of the current state parameters ($stateParams).
|
||||
* @param {Object} newParams The set of parameters which will be composited with inherited params.
|
||||
* @param {Object} $current Internal definition of object representing the current state.
|
||||
* @param {Object} $to Internal definition of object representing state to transition to.
|
||||
*/
|
||||
function inheritParams(currentParams, newParams, $current, $to) {
|
||||
var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
|
||||
|
||||
for (var i in parents) {
|
||||
if (!parents[i].params || !parents[i].params.length) continue;
|
||||
parentParams = parents[i].params;
|
||||
|
||||
for (var j in parentParams) {
|
||||
if (arraySearch(inheritList, parentParams[j]) >= 0) continue;
|
||||
inheritList.push(parentParams[j]);
|
||||
inherited[parentParams[j]] = currentParams[parentParams[j]];
|
||||
}
|
||||
}
|
||||
return extend({}, inherited, newParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a set of values to string or `null`, filtering them by a list of keys.
|
||||
*
|
||||
* @param {Array} keys The list of keys to normalize/return.
|
||||
* @param {Object} values An object hash of values to normalize.
|
||||
* @return {Object} Returns an object hash of normalized string values.
|
||||
*/
|
||||
function normalize(keys, values) {
|
||||
var normalized = {};
|
||||
|
||||
forEach(keys, function (name) {
|
||||
var value = values[name];
|
||||
normalized[name] = (value != null) ? String(value) : null;
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
|
||||
*
|
||||
* @param {Object} a The first object.
|
||||
* @param {Object} b The second object.
|
||||
* @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
|
||||
* it defaults to the list of keys in `a`.
|
||||
* @return {Boolean} Returns `true` if the keys match, otherwise `false`.
|
||||
*/
|
||||
function equalForKeys(a, b, keys) {
|
||||
if (!keys) {
|
||||
keys = [];
|
||||
for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
|
||||
}
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var k = keys[i];
|
||||
if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of an object, based on a list of keys.
|
||||
*
|
||||
* @param {Array} keys
|
||||
* @param {Object} values
|
||||
* @return {Boolean} Returns a subset of `values`.
|
||||
*/
|
||||
function filterByKeys(keys, values) {
|
||||
var filtered = {};
|
||||
|
||||
forEach(keys, function (name) {
|
||||
filtered[name] = values[name];
|
||||
});
|
||||
return filtered;
|
||||
}
|
||||
|
||||
angular.module('ui.router.util', ['ng']);
|
||||
angular.module('ui.router.router', ['ui.router.util']);
|
||||
angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
|
||||
angular.module('ui.router', ['ui.router.state']);
|
||||
angular.module('ui.router.compat', ['ui.router']);
|
||||
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Index</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Index</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3> </h3>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,265 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: resolve.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: resolve.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source"><code>/**
|
||||
* Service (`ui-util`). Manages resolution of (acyclic) graphs of promises.
|
||||
* @module $resolve
|
||||
* @requires $q
|
||||
* @requires $injector
|
||||
*/
|
||||
$Resolve.$inject = ['$q', '$injector'];
|
||||
function $Resolve( $q, $injector) {
|
||||
|
||||
var VISIT_IN_PROGRESS = 1,
|
||||
VISIT_DONE = 2,
|
||||
NOTHING = {},
|
||||
NO_DEPENDENCIES = [],
|
||||
NO_LOCALS = NOTHING,
|
||||
NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
|
||||
|
||||
|
||||
/**
|
||||
* Studies a set of invocables that are likely to be used multiple times.
|
||||
* $resolve.study(invocables)(locals, parent, self)
|
||||
* is equivalent to
|
||||
* $resolve.resolve(invocables, locals, parent, self)
|
||||
* but the former is more efficient (in fact `resolve` just calls `study` internally).
|
||||
* See {@link module:$resolve/resolve} for details.
|
||||
* @function
|
||||
* @param {Object} invocables
|
||||
* @return {Function}
|
||||
*/
|
||||
this.study = function (invocables) {
|
||||
if (!isObject(invocables)) throw new Error("'invocables' must be an object");
|
||||
|
||||
// Perform a topological sort of invocables to build an ordered plan
|
||||
var plan = [], cycle = [], visited = {};
|
||||
function visit(value, key) {
|
||||
if (visited[key] === VISIT_DONE) return;
|
||||
|
||||
cycle.push(key);
|
||||
if (visited[key] === VISIT_IN_PROGRESS) {
|
||||
cycle.splice(0, cycle.indexOf(key));
|
||||
throw new Error("Cyclic dependency: " + cycle.join(" -> "));
|
||||
}
|
||||
visited[key] = VISIT_IN_PROGRESS;
|
||||
|
||||
if (isString(value)) {
|
||||
plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
|
||||
} else {
|
||||
var params = $injector.annotate(value);
|
||||
forEach(params, function (param) {
|
||||
if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
|
||||
});
|
||||
plan.push(key, value, params);
|
||||
}
|
||||
|
||||
cycle.pop();
|
||||
visited[key] = VISIT_DONE;
|
||||
}
|
||||
forEach(invocables, visit);
|
||||
invocables = cycle = visited = null; // plan is all that's required
|
||||
|
||||
function isResolve(value) {
|
||||
return isObject(value) && value.then && value.$$promises;
|
||||
}
|
||||
|
||||
return function (locals, parent, self) {
|
||||
if (isResolve(locals) && self === undefined) {
|
||||
self = parent; parent = locals; locals = null;
|
||||
}
|
||||
if (!locals) locals = NO_LOCALS;
|
||||
else if (!isObject(locals)) {
|
||||
throw new Error("'locals' must be an object");
|
||||
}
|
||||
if (!parent) parent = NO_PARENT;
|
||||
else if (!isResolve(parent)) {
|
||||
throw new Error("'parent' must be a promise returned by $resolve.resolve()");
|
||||
}
|
||||
|
||||
// To complete the overall resolution, we have to wait for the parent
|
||||
// promise and for the promise for each invokable in our plan.
|
||||
var resolution = $q.defer(),
|
||||
result = resolution.promise,
|
||||
promises = result.$$promises = {},
|
||||
values = extend({}, locals),
|
||||
wait = 1 + plan.length/3,
|
||||
merged = false;
|
||||
|
||||
function done() {
|
||||
// Merge parent values we haven't got yet and publish our own $$values
|
||||
if (!--wait) {
|
||||
if (!merged) merge(values, parent.$$values);
|
||||
result.$$values = values;
|
||||
result.$$promises = true; // keep for isResolve()
|
||||
resolution.resolve(values);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(reason) {
|
||||
result.$$failure = reason;
|
||||
resolution.reject(reason);
|
||||
}
|
||||
|
||||
// Short-circuit if parent has already failed
|
||||
if (isDefined(parent.$$failure)) {
|
||||
fail(parent.$$failure);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Merge parent values if the parent has already resolved, or merge
|
||||
// parent promises and wait if the parent resolve is still in progress.
|
||||
if (parent.$$values) {
|
||||
merged = merge(values, parent.$$values);
|
||||
done();
|
||||
} else {
|
||||
extend(promises, parent.$$promises);
|
||||
parent.then(done, fail);
|
||||
}
|
||||
|
||||
// Process each invocable in the plan, but ignore any where a local of the same name exists.
|
||||
for (var i=0, ii=plan.length; i<ii; i+=3) {
|
||||
if (locals.hasOwnProperty(plan[i])) done();
|
||||
else invoke(plan[i], plan[i+1], plan[i+2]);
|
||||
}
|
||||
|
||||
function invoke(key, invocable, params) {
|
||||
// Create a deferred for this invocation. Failures will propagate to the resolution as well.
|
||||
var invocation = $q.defer(), waitParams = 0;
|
||||
function onfailure(reason) {
|
||||
invocation.reject(reason);
|
||||
fail(reason);
|
||||
}
|
||||
// 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).
|
||||
forEach(params, function (dep) {
|
||||
if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
|
||||
waitParams++;
|
||||
promises[dep].then(function (result) {
|
||||
values[dep] = result;
|
||||
if (!(--waitParams)) proceed();
|
||||
}, onfailure);
|
||||
}
|
||||
});
|
||||
if (!waitParams) proceed();
|
||||
function proceed() {
|
||||
if (isDefined(result.$$failure)) return;
|
||||
try {
|
||||
invocation.resolve($injector.invoke(invocable, self, values));
|
||||
invocation.promise.then(function (result) {
|
||||
values[key] = result;
|
||||
done();
|
||||
}, onfailure);
|
||||
} catch (e) {
|
||||
onfailure(e);
|
||||
}
|
||||
}
|
||||
// Publish promise synchronously; invocations further down in the plan may depend on it.
|
||||
promises[key] = invocation.promise;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a set of invocables. An invocable is a function to be invoked via `$injector.invoke()`,
|
||||
* and can have an arbitrary number of dependencies. An invocable can either return a value directly,
|
||||
* or a `$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.
|
||||
*
|
||||
* @function
|
||||
* @param {Object.<string, Function|string>} invocables functions to invoke or `$injector` services to fetch.
|
||||
* @param {Object.<string, *>} [locals] values to make available to the injectables
|
||||
* @param {Promise.<Object>} [parent] a promise returned by another call to `$resolve`.
|
||||
* @param {Object} [self] the `this` for the invoked methods
|
||||
* @return {Promise.<Object>} Promise for an object that contains the resolved return value
|
||||
* of all invocables, as well as any inherited and local values.
|
||||
*/
|
||||
this.resolve = function (invocables, locals, parent, self) {
|
||||
return this.study(invocables)(locals, parent, self);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$resolve', $Resolve);
|
||||
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
(function() {
|
||||
var counter = 0;
|
||||
var numbered;
|
||||
var source = document.getElementsByClassName('prettyprint source');
|
||||
|
||||
if (source && source[0]) {
|
||||
source = source[0].getElementsByTagName('code')[0];
|
||||
|
||||
numbered = source.innerHTML.split('\n');
|
||||
numbered = numbered.map(function(item) {
|
||||
counter++;
|
||||
return '<span id="line' + counter + '" class="line"></span>' + item;
|
||||
});
|
||||
|
||||
source.innerHTML = numbered.join('\n');
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
|
||||
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
|
||||
@@ -0,0 +1,28 @@
|
||||
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
|
||||
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
|
||||
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
|
||||
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
|
||||
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
|
||||
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
|
||||
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
|
||||
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
|
||||
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
|
||||
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
|
||||
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
|
||||
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
|
||||
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
|
||||
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
|
||||
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
|
||||
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
|
||||
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
|
||||
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
|
||||
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
|
||||
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
|
||||
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
|
||||
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
|
||||
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
|
||||
@@ -0,0 +1,290 @@
|
||||
html
|
||||
{
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
font: 14px "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans serif;
|
||||
line-height: 130%;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
header
|
||||
{
|
||||
display: block;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
|
||||
.class-description {
|
||||
font-style: italic;
|
||||
font-family: Palatino, 'Palatino Linotype', serif;
|
||||
font-size: 130%;
|
||||
line-height: 140%;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#main {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section
|
||||
{
|
||||
display: block;
|
||||
|
||||
background-color: #fff;
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin-right: 240px;
|
||||
}
|
||||
|
||||
.variation {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.optional:after {
|
||||
content: "opt";
|
||||
font-size: 60%;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
nav
|
||||
{
|
||||
display: block;
|
||||
float: left;
|
||||
margin-left: -230px;
|
||||
margin-top: 28px;
|
||||
width: 220px;
|
||||
border-left: 1px solid #ccc;
|
||||
padding-left: 9px;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
|
||||
font-size: 100%;
|
||||
line-height: 17px;
|
||||
padding:0;
|
||||
margin:0;
|
||||
list-style-type:none;
|
||||
}
|
||||
|
||||
nav h2 a, nav h2 a:visited {
|
||||
color: #A35A00;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
nav h3 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
nav li {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: #5C5954;
|
||||
}
|
||||
|
||||
nav a:visited {
|
||||
color: #5C5954;
|
||||
}
|
||||
|
||||
nav a:active {
|
||||
color: #5C5954;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: block;
|
||||
padding: 6px;
|
||||
margin-top: 12px;
|
||||
font-style: italic;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: 200%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 6px 0 9px 0;
|
||||
}
|
||||
|
||||
h2
|
||||
{
|
||||
font-size: 170%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 6px 0 3px 0;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
font-size: 150%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin-top: 16px;
|
||||
margin: 6px 0 3px 0;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
font-size: 130%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin-top: 16px;
|
||||
margin: 18px 0 3px 0;
|
||||
color: #A35A00;
|
||||
}
|
||||
|
||||
h5, .container-overview .subsection-title
|
||||
{
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 8px 0 3px -16px;
|
||||
}
|
||||
|
||||
h6
|
||||
{
|
||||
font-size: 100%;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 6px 0 3px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.ancestors { color: #999; }
|
||||
.ancestors a
|
||||
{
|
||||
color: #999 !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.important
|
||||
{
|
||||
font-weight: bold;
|
||||
color: #950B02;
|
||||
}
|
||||
|
||||
.yes-def {
|
||||
text-indent: -1000px;
|
||||
}
|
||||
|
||||
.type-signature {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.name, .signature {
|
||||
font-family: Consolas, "Lucida Console", Monaco, monospace;
|
||||
}
|
||||
|
||||
.details { margin-top: 14px; border-left: 2px solid #DDD; }
|
||||
.details dt { width:100px; float:left; padding-left: 10px; padding-top: 6px; }
|
||||
.details dd { margin-left: 50px; }
|
||||
.details ul { margin: 0; }
|
||||
.details ul { list-style-type: none; }
|
||||
.details li { margin-left: 30px; padding-top: 6px; }
|
||||
.details pre.prettyprint { margin: 0 }
|
||||
.details .object-value { padding-top: 0; }
|
||||
|
||||
.description {
|
||||
margin-bottom: 1em;
|
||||
margin-left: -16px;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.code-caption
|
||||
{
|
||||
font-style: italic;
|
||||
font-family: Palatino, 'Palatino Linotype', serif;
|
||||
font-size: 107%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.prettyprint
|
||||
{
|
||||
border: 1px solid #ddd;
|
||||
width: 80%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prettyprint.source {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.prettyprint code
|
||||
{
|
||||
font-family: Consolas, 'Lucida Console', Monaco, monospace;
|
||||
font-size: 100%;
|
||||
line-height: 18px;
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
margin: 0;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
border-left: 3px #ddd solid;
|
||||
}
|
||||
|
||||
.prettyprint code span.line
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.params, .props
|
||||
{
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.params .name, .props .name, .name code {
|
||||
color: #A35A00;
|
||||
font-family: Consolas, 'Lucida Console', Monaco, monospace;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.params td, .params th, .props td, .props th
|
||||
{
|
||||
border: 1px solid #ddd;
|
||||
margin: 0px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
padding: 4px 6px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.params thead tr, .props thead tr
|
||||
{
|
||||
background-color: #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.params .params thead tr, .props .props thead tr
|
||||
{
|
||||
background-color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.params th, .props th { border-right: 1px solid #aaa; }
|
||||
.params thead .last, .props thead .last { border-right: 1px solid #ddd; }
|
||||
|
||||
.disabled {
|
||||
color: #454545;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/* JSDoc prettify.js theme */
|
||||
|
||||
/* plain text */
|
||||
.pln {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* string content */
|
||||
.str {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a keyword */
|
||||
.kwd {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a comment */
|
||||
.com {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* a type name */
|
||||
.typ {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a literal value */
|
||||
.lit {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* punctuation */
|
||||
.pun {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* lisp open bracket */
|
||||
.opn {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* lisp close bracket */
|
||||
.clo {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup tag name */
|
||||
.tag {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup attribute name */
|
||||
.atn {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup attribute value */
|
||||
.atv {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a declaration */
|
||||
.dec {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a variable name */
|
||||
.var {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a function name */
|
||||
.fun {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/* Tomorrow Theme */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* Pretty printing styles. Used with prettify.js. */
|
||||
/* SPAN elements with the classes below are added by prettyprint. */
|
||||
/* plain text */
|
||||
.pln {
|
||||
color: #4d4d4c; }
|
||||
|
||||
@media screen {
|
||||
/* string content */
|
||||
.str {
|
||||
color: #718c00; }
|
||||
|
||||
/* a keyword */
|
||||
.kwd {
|
||||
color: #8959a8; }
|
||||
|
||||
/* a comment */
|
||||
.com {
|
||||
color: #8e908c; }
|
||||
|
||||
/* a type name */
|
||||
.typ {
|
||||
color: #4271ae; }
|
||||
|
||||
/* a literal value */
|
||||
.lit {
|
||||
color: #f5871f; }
|
||||
|
||||
/* punctuation */
|
||||
.pun {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* lisp open bracket */
|
||||
.opn {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* lisp close bracket */
|
||||
.clo {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* a markup tag name */
|
||||
.tag {
|
||||
color: #c82829; }
|
||||
|
||||
/* a markup attribute name */
|
||||
.atn {
|
||||
color: #f5871f; }
|
||||
|
||||
/* a markup attribute value */
|
||||
.atv {
|
||||
color: #3e999f; }
|
||||
|
||||
/* a declaration */
|
||||
.dec {
|
||||
color: #f5871f; }
|
||||
|
||||
/* a variable name */
|
||||
.var {
|
||||
color: #c82829; }
|
||||
|
||||
/* a function name */
|
||||
.fun {
|
||||
color: #4271ae; } }
|
||||
/* Use higher contrast and text-weight for printable form. */
|
||||
@media print, projection {
|
||||
.str {
|
||||
color: #060; }
|
||||
|
||||
.kwd {
|
||||
color: #006;
|
||||
font-weight: bold; }
|
||||
|
||||
.com {
|
||||
color: #600;
|
||||
font-style: italic; }
|
||||
|
||||
.typ {
|
||||
color: #404;
|
||||
font-weight: bold; }
|
||||
|
||||
.lit {
|
||||
color: #044; }
|
||||
|
||||
.pun, .opn, .clo {
|
||||
color: #440; }
|
||||
|
||||
.tag {
|
||||
color: #006;
|
||||
font-weight: bold; }
|
||||
|
||||
.atn {
|
||||
color: #404; }
|
||||
|
||||
.atv {
|
||||
color: #060; } }
|
||||
/* Style */
|
||||
/*
|
||||
pre.prettyprint {
|
||||
background: white;
|
||||
font-family: Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px; }
|
||||
*/
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0; }
|
||||
|
||||
/* IE indents via margin-left */
|
||||
li.L0,
|
||||
li.L1,
|
||||
li.L2,
|
||||
li.L3,
|
||||
li.L4,
|
||||
li.L5,
|
||||
li.L6,
|
||||
li.L7,
|
||||
li.L8,
|
||||
li.L9 {
|
||||
/* */ }
|
||||
|
||||
/* Alternate shading for lines */
|
||||
li.L1,
|
||||
li.L3,
|
||||
li.L5,
|
||||
li.L7,
|
||||
li.L9 {
|
||||
/* */ }
|
||||
@@ -0,0 +1,137 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: templateFactory.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: templateFactory.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source"><code>/**
|
||||
* Service. Manages loading of templates.
|
||||
* @constructor
|
||||
* @name $templateFactory
|
||||
* @requires $http
|
||||
* @requires $templateCache
|
||||
* @requires $injector
|
||||
*/
|
||||
$TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
|
||||
function $TemplateFactory( $http, $templateCache, $injector) {
|
||||
|
||||
/**
|
||||
* Creates a template from a configuration object.
|
||||
* @function
|
||||
* @name $templateFactory#fromConfig
|
||||
* @methodOf $templateFactory
|
||||
* @param {Object} config Configuration object for which to load a template. The following
|
||||
* properties are search in the specified order, and the first one that is defined is
|
||||
* used to create the template:
|
||||
* @param {string|Function} config.template html string template or function to load via
|
||||
* {@link $templateFactory#fromString fromString}.
|
||||
* @param {string|Function} config.templateUrl url to load or a function returning the url
|
||||
* to load via {@link $templateFactory#fromUrl fromUrl}.
|
||||
* @param {Function} config.templateProvider function to invoke via
|
||||
* {@link $templateFactory#fromProvider fromProvider}.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @param {Object} [locals] Locals to pass to `invoke` if the template is loaded via a
|
||||
* `templateProvider`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string,
|
||||
* or `null` if no template is configured.
|
||||
*/
|
||||
this.fromConfig = function (config, params, locals) {
|
||||
return (
|
||||
isDefined(config.template) ? this.fromString(config.template, params) :
|
||||
isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
|
||||
isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template from a string or a function returning a string.
|
||||
* @function
|
||||
* @name $templateFactory#fromString
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} template html template as a string or function that returns an html
|
||||
* template as a string.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromString = function (template, params) {
|
||||
return isFunction(template) ? template(params) : template;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a template from the a URL via `$http` and `$templateCache`.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} url url of the template to load, or a function that returns a url.
|
||||
* @param {Object} params Parameters to pass to the url function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromUrl = function (url, params) {
|
||||
if (isFunction(url)) url = url(params);
|
||||
if (url == null) return null;
|
||||
else return $http
|
||||
.get(url, { cache: $templateCache })
|
||||
.then(function(response) { return response.data; });
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template by invoking an injectable provider function.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {Function} provider Function to invoke via `$injector.invoke`
|
||||
* @param {Object} params Parameters for the template.
|
||||
* @param {Object} [locals] Locals to pass to `invoke`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromProvider = function (provider, params, locals) {
|
||||
return $injector.invoke(provider, null, locals || { params: params });
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: urlMatcherFactory.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: urlMatcherFactory.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source"><code>/**
|
||||
* Matches URLs against patterns and extracts named parameters from the path or the search
|
||||
* part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
|
||||
* of search parameters. Multiple search parameter names are separated by '&'. Search parameters
|
||||
* do not influence whether or not a URL is matched, but their values are passed through into
|
||||
* the matched parameters returned by {@link UrlMatcher#exec exec}.
|
||||
*
|
||||
* Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
|
||||
* syntax, which optionally allows a regular expression for the parameter to be specified:
|
||||
*
|
||||
* * ':' name - colon placeholder
|
||||
* * '*' name - catch-all placeholder
|
||||
* * '{' name '}' - curly placeholder
|
||||
* * '{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain
|
||||
* curly braces, they must be in matched pairs or escaped with a backslash.
|
||||
*
|
||||
* Parameter names may contain only word characters (latin letters, digits, and underscore) and
|
||||
* must be unique within the pattern (across both path and search parameters). For colon
|
||||
* placeholders or curly placeholders without an explicit regexp, a path parameter matches any
|
||||
* number of characters other than '/'. For catch-all placeholders the path parameter matches
|
||||
* any number of characters.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* * '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for
|
||||
* trailing slashes, and patterns have to match the entire path, not just a prefix.
|
||||
* * '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
|
||||
* '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
|
||||
* * '/user/{id}' - Same as the previous example, but using curly brace syntax.
|
||||
* * '/user/{id:[^/]*}' - Same as the previous example.
|
||||
* * '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id
|
||||
* parameter consists of 1 to 8 hex digits.
|
||||
* * '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the
|
||||
* path into the parameter 'path'.
|
||||
* * '/files/*path' - ditto.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} pattern the pattern to compile into a matcher.
|
||||
*
|
||||
* @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
|
||||
* URL matching this matcher (i.e. any string for which {@link UrlMatcher#exec exec()} returns
|
||||
* non-null) will start with this prefix.
|
||||
*/
|
||||
function UrlMatcher(pattern) {
|
||||
|
||||
// Find all placeholders and create a compiled pattern, using either classic or curly syntax:
|
||||
// '*' name
|
||||
// ':' name
|
||||
// '{' name '}'
|
||||
// '{' name ':' regexp '}'
|
||||
// The regular expression is somewhat complicated due to the need to allow curly braces
|
||||
// inside the regular expression. The placeholder regexp breaks down as follows:
|
||||
// ([:*])(\w+) classic placeholder ($1 / $2)
|
||||
// \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
|
||||
// (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
|
||||
// [^{}\\]+ - anything other than curly braces or backslash
|
||||
// \\. - a backslash escape
|
||||
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
|
||||
var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
|
||||
names = {}, compiled = '^', last = 0, m,
|
||||
segments = this.segments = [],
|
||||
params = this.params = [];
|
||||
|
||||
function addParameter(id) {
|
||||
if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
names[id] = true;
|
||||
params.push(id);
|
||||
}
|
||||
|
||||
function quoteRegExp(string) {
|
||||
return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
|
||||
}
|
||||
|
||||
this.source = pattern;
|
||||
|
||||
// Split into static segments separated by path parameter placeholders.
|
||||
// The number of segments is always 1 more than the number of parameters.
|
||||
var id, regexp, segment;
|
||||
while ((m = placeholder.exec(pattern))) {
|
||||
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
|
||||
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
|
||||
segment = pattern.substring(last, m.index);
|
||||
if (segment.indexOf('?') >= 0) break; // we're into the search part
|
||||
compiled += quoteRegExp(segment) + '(' + regexp + ')';
|
||||
addParameter(id);
|
||||
segments.push(segment);
|
||||
last = placeholder.lastIndex;
|
||||
}
|
||||
segment = pattern.substring(last);
|
||||
|
||||
// Find any search parameter names and remove them from the last segment
|
||||
var i = segment.indexOf('?');
|
||||
if (i >= 0) {
|
||||
var search = this.sourceSearch = segment.substring(i);
|
||||
segment = segment.substring(0, i);
|
||||
this.sourcePath = pattern.substring(0, last+i);
|
||||
|
||||
// Allow parameters to be separated by '?' as well as '&' to make concat() easier
|
||||
forEach(search.substring(1).split(/[&?]/), addParameter);
|
||||
} else {
|
||||
this.sourcePath = pattern;
|
||||
this.sourceSearch = '';
|
||||
}
|
||||
|
||||
compiled += quoteRegExp(segment) + '$';
|
||||
segments.push(segment);
|
||||
this.regexp = new RegExp(compiled);
|
||||
this.prefix = segments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new matcher for a pattern constructed by appending the path part and adding the
|
||||
* search parameters of the specified pattern to this pattern. The current pattern is not
|
||||
* modified. This can be understood as creating a pattern for URLs that are relative to (or
|
||||
* suffixes of) the current pattern.
|
||||
*
|
||||
* ### Example
|
||||
* The following two matchers are equivalent:
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').concat('/details?date');
|
||||
* new UrlMatcher('/user/{id}/details?q&date');
|
||||
* ```
|
||||
*
|
||||
* @param {string} pattern The pattern to append.
|
||||
* @return {UrlMatcher} A matcher for the concatenated pattern.
|
||||
*/
|
||||
UrlMatcher.prototype.concat = function (pattern) {
|
||||
// Because order of search parameters is irrelevant, we can add our own search
|
||||
// parameters to the end of the new pattern. Parse the new pattern by itself
|
||||
// and then join the bits together, but it's much easier to do this on a string level.
|
||||
return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch);
|
||||
};
|
||||
|
||||
UrlMatcher.prototype.toString = function () {
|
||||
return this.source;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the specified path against this matcher, and returns an object containing the captured
|
||||
* parameter values, or null if the path does not match. The returned object contains the values
|
||||
* of any search parameters that are mentioned in the pattern, but their value may be null if
|
||||
* they are not present in `searchParams`. This means that search parameters are always treated
|
||||
* as optional.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
|
||||
* // returns { id:'bob', q:'hello', r:null }
|
||||
* ```
|
||||
*
|
||||
* @param {string} path The URL path to match, e.g. `$location.path()`.
|
||||
* @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
|
||||
* @return {Object} The captured parameter values.
|
||||
*/
|
||||
UrlMatcher.prototype.exec = function (path, searchParams) {
|
||||
var m = this.regexp.exec(path);
|
||||
if (!m) return null;
|
||||
|
||||
var params = this.params, nTotal = params.length,
|
||||
nPath = this.segments.length-1,
|
||||
values = {}, i;
|
||||
|
||||
if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
|
||||
|
||||
for (i=0; i<nPath; i++) values[params[i]] = m[i+1];
|
||||
for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the names of all path and search parameters of this pattern in an unspecified order.
|
||||
* @return {Array.<string>} An array of parameter names. Must be treated as read-only. If the
|
||||
* pattern has no parameters, an empty array is returned.
|
||||
*/
|
||||
UrlMatcher.prototype.parameters = function () {
|
||||
return this.params;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a URL that matches this pattern by substituting the specified values
|
||||
* for the path and search parameters. Null values for path parameters are
|
||||
* treated as empty strings.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
|
||||
* // returns '/user/bob?q=yes'
|
||||
* ```
|
||||
*
|
||||
* @param {Object} values the values to substitute for the parameters in this pattern.
|
||||
* @return {string} the formatted URL (path and optionally search part).
|
||||
*/
|
||||
UrlMatcher.prototype.format = function (values) {
|
||||
var segments = this.segments, params = this.params;
|
||||
if (!values) return segments.join('');
|
||||
|
||||
var nPath = segments.length-1, nTotal = params.length,
|
||||
result = segments[0], i, search, value;
|
||||
|
||||
for (i=0; i<nPath; i++) {
|
||||
value = values[params[i]];
|
||||
// TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
|
||||
if (value != null) result += encodeURIComponent(value);
|
||||
result += segments[i+1];
|
||||
}
|
||||
for (/**/; i<nTotal; i++) {
|
||||
value = values[params[i]];
|
||||
if (value != null) {
|
||||
result += (search ? '&' : '?') + params[i] + '=' + encodeURIComponent(value);
|
||||
search = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Service. Factory for {@link UrlMatcher} instances. The factory is also available to providers
|
||||
* under the name `$urlMatcherFactoryProvider`.
|
||||
* @constructor
|
||||
* @name $urlMatcherFactory
|
||||
*/
|
||||
function $UrlMatcherFactory() {
|
||||
/**
|
||||
* Creates a {@link UrlMatcher} for the specified pattern.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#compile
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {string} pattern The URL pattern.
|
||||
* @return {UrlMatcher} The UrlMatcher.
|
||||
*/
|
||||
this.compile = function (pattern) {
|
||||
return new UrlMatcher(pattern);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the specified object is a UrlMatcher, or false otherwise.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#isMatcher
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {Object} o
|
||||
* @return {boolean}
|
||||
*/
|
||||
this.isMatcher = function (o) {
|
||||
return isObject(o) && isFunction(o.exec) && isFunction(o.format) && isFunction(o.concat);
|
||||
};
|
||||
|
||||
this.$get = function () {
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
// Register as a provider so it's available to other providers
|
||||
angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="$resolve.html">$resolve</a></li></ul><h3>Classes</h3><ul><li><a href="$templateFactory.html">$templateFactory</a></li><li><a href="$urlMatcherFactory.html">$urlMatcherFactory</a></li><li><a href="UrlMatcher.html">UrlMatcher</a></li></ul><h3>Global</h3><ul><li><a href="global.html#ancestors">ancestors</a></li><li><a href="global.html#arraySearch">arraySearch</a></li><li><a href="global.html#equalForKeys">equalForKeys</a></li><li><a href="global.html#filterByKeys">filterByKeys</a></li><li><a href="global.html#inheritParams">inheritParams</a></li><li><a href="global.html#keys">keys</a></li><li><a href="global.html#normalize">normalize</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br clear="both">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Dec 09 2013 13:59:32 GMT-0500 (EST)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<h2>{{contact.name}}</h2>
|
||||
<ul>
|
||||
<li ng-repeat="item in contact.items">
|
||||
|
||||
<!-- Here's another ui-sref except THIS time we are passing parameters
|
||||
AND inheriting parameters from active ancestor states. So we don't
|
||||
need to resupply the contactId parameter. -->
|
||||
<a ui-sref="contacts.detail.item({itemId:item.id})">{{item.type}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div ui-view ng-animate="{enter:'fade-enter'}">
|
||||
<!-- Example of default content. This content will be replace as soon as
|
||||
this ui-view is populate with a template -->
|
||||
<small class="muted">Click on a contact item to view and/or edit it.</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<hr>
|
||||
<h4>{{item.type}} <button class="btn" ng-click="done()">Done</button></h4>
|
||||
<div><input type="text" ng-model="item.value" /></div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<hr>
|
||||
<h4>{{item.type}} <button class="btn" ng-click="edit()">Edit</button></h4>
|
||||
<div>{{item.value}}</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="row">
|
||||
<div class="span3">
|
||||
<div class="pa-sidebar well well-small">
|
||||
<ul class="nav nav-list">
|
||||
<li ng-class="{ active: $state.includes('contacts.list') }"><a ui-sref="contacts.list">All Contacts</a></li>
|
||||
<li class="nav-header">Top Contacts</li>
|
||||
|
||||
<!-- This <li> will only add the 'active' class if 'contacts.detail' or its descendants are active
|
||||
AND if it is the link for the active contact (aka contactId) -->
|
||||
<li ng-repeat="contact in contacts | limitTo:2"
|
||||
ng-class="{ active: $state.includes('contacts.detail') && $stateParams.contactId == contact.id }">
|
||||
|
||||
<!-- Here's a ui-sref that is also providing necessary parameters -->
|
||||
<a ui-sref="contacts.detail({contactId:contact.id})">{{contact.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<button class="btn" ng-click="goToRandom()">Show random contact</button>
|
||||
|
||||
<!-- Another named view -->
|
||||
<div ui-view="menuTip"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Our unnamed main ui-view for this template -->
|
||||
<div ui-view class="span9" ng-animate="{enter:'fade-enter'}"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"contacts":[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Alice",
|
||||
"items": [
|
||||
{
|
||||
"id": "a",
|
||||
"type": "phone number",
|
||||
"value": "555-1234-1234"
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"type": "email",
|
||||
"value": "alice@mailinator.com"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 42,
|
||||
"name": "Bob",
|
||||
"items": [
|
||||
{
|
||||
"id": "a",
|
||||
"type": "blog",
|
||||
"value": "http://bob.blogger.com"
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"type": "fax",
|
||||
"value": "555-999-9999"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 123,
|
||||
"name": "Eve",
|
||||
"items": [
|
||||
{
|
||||
"id": "a",
|
||||
"type": "full name",
|
||||
"value": "Eve Adamsdottir"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<h2>All Contacts</h2>
|
||||
<ul>
|
||||
<li ng-repeat="contact in contacts">
|
||||
<a ui-sref="contacts.detail({contactId:contact.id})">{{contact.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
angular.module('uiRouterSample')
|
||||
|
||||
// A RESTful factory for retreiving contacts from 'contacts.json'
|
||||
.factory('contacts', ['$http', function ($http, utils) {
|
||||
var path = 'contacts.json';
|
||||
var contacts = $http.get(path).then(function (resp) {
|
||||
return resp.data.contacts;
|
||||
});
|
||||
|
||||
var factory = {};
|
||||
factory.all = function () {
|
||||
return contacts;
|
||||
};
|
||||
factory.get = function (id) {
|
||||
return contacts.then(function(){
|
||||
return utils.findById(contacts, id);
|
||||
})
|
||||
};
|
||||
return factory;
|
||||
}])
|
||||
|
||||
.factory('utils', function () {
|
||||
|
||||
return {
|
||||
|
||||
// Util for finding an object by its 'id' property among an array
|
||||
findById: function findById(a, id) {
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i].id == id) return a[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
// Util for returning a randomKey from a collection that also isn't the current key
|
||||
newRandomKey: function newRandomKey(coll, key, currentKey){
|
||||
var randKey;
|
||||
do {
|
||||
randKey = coll[Math.floor(coll.length * Math.random())][key];
|
||||
} while (randKey == currentKey);
|
||||
return randKey;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
<!doctype html>
|
||||
<!-- Our uiRouterSample module defined here -->
|
||||
<html lang="en" ng-app="uiRouterSample">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- using twitter bootstrap, but of course -->
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap.min.css">
|
||||
<!-- styles for ng-animate are located here -->
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
|
||||
<!-- Include both angular.js and angular-ui-router.js-->
|
||||
<script src="../lib/angular-1.1.5.js"></script>
|
||||
<script src="../build/angular-ui-router.js"></script>
|
||||
|
||||
<!-- module.js declares the uiRouterSample module and adds items to $rootScope-->
|
||||
<script src="module.js"></script>
|
||||
<!-- factory.js defines a 'contacts' http service and a 'utils' global functions service -->
|
||||
<script src="factory.js"></script>
|
||||
<!-- state.js takes care of all the state configuration, where all the magic's at-->
|
||||
<script src="states.js"></script>
|
||||
|
||||
<!-- could easily use a custom property of the state here instead of 'name' -->
|
||||
<title ng-bind="$state.current.name + ' - ui-router'">ui-router</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner"><div class="container">
|
||||
|
||||
<!-- ui-sref is a great directive for linking a state location with an anchor link.
|
||||
You should almost always use ui-sref instead of href on your links when you want
|
||||
then to navigate to a state. When this link is clicked it will take the application
|
||||
to the 'home' state. Behind the scenes the directive also adds the correct href attr
|
||||
and url. -->
|
||||
<a class="brand" ui-sref="home">ui-router</a>
|
||||
<ul class="nav">
|
||||
|
||||
<!-- Here you can see ui-sref in action again. Also notice the use of $state.includes, which
|
||||
will set the links to 'active' if, for example on the first link, 'contacts' or any of
|
||||
its descendant states are activated. -->
|
||||
<li ng-class="{ active: $state.includes('contacts') }"><a ui-sref="contacts.list">Contacts</a></li>
|
||||
<li ng-class="{ active: $state.includes('about') }"><a ui-sref="about">About</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Here is a named ui-view. ui-views don't have to be named, but we'll be populate this
|
||||
on from various different child states and we want a name to help us target. -->
|
||||
<p ui-view="hint" class="navbar-text pull-right"></p>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<!-- Here is the main ui-view (unnamed) and will be populate by its immediate children's templates
|
||||
unless otherwise explicitly named views are targeted. It's also employing ng-animate. -->
|
||||
<div ui-view class="container" style="margin-top:60px" ng-animate="{enter:'fade-enter'}"></div>
|
||||
|
||||
|
||||
<hr>
|
||||
<pre>
|
||||
<!-- Here's some values to keep an eye on in the sample in order to understand $state and $stateParams -->
|
||||
$state = {{$state.current.name}}
|
||||
$stateParams = {{$stateParams}}
|
||||
$state full url = {{ $state.$current.url.source }}
|
||||
<!-- $state.$current is not a public api, we are using it to
|
||||
display the full url for learning purposes-->
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Make sure to include the `ui.router` module as a dependency
|
||||
angular.module('uiRouterSample', ['ui.router'])
|
||||
.run(
|
||||
[ '$rootScope', '$state', '$stateParams',
|
||||
function ($rootScope, $state, $stateParams) {
|
||||
|
||||
// It's very handy to add references to $state and $stateParams to the $rootScope
|
||||
// so that you can access them from any scope within your applications.For example,
|
||||
// <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
|
||||
// to active whenever 'contacts.list' or one of its decendents is active.
|
||||
$rootScope.$state = $state;
|
||||
$rootScope.$stateParams = $stateParams;
|
||||
}]);
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
// Make sure to include the `ui.router` module as a dependency.
|
||||
angular.module('uiRouterSample')
|
||||
.config(
|
||||
[ '$stateProvider', '$urlRouterProvider',
|
||||
function ($stateProvider, $urlRouterProvider) {
|
||||
|
||||
/////////////////////////////
|
||||
// Redirects and Otherwise //
|
||||
/////////////////////////////
|
||||
|
||||
// Use $urlRouterProvider to configure any redirects (when) and invalid urls (otherwise).
|
||||
$urlRouterProvider
|
||||
|
||||
// The `when` method says if the url is ever the 1st param, then redirect to the 2nd param
|
||||
// Here we are just setting up some convenience urls.
|
||||
.when('/c?id', '/contacts/:id')
|
||||
.when('/user/:id', '/contacts/:id')
|
||||
|
||||
// If the url is ever invalid, e.g. '/asdf', then redirect to '/' aka the home state
|
||||
.otherwise('/');
|
||||
|
||||
|
||||
//////////////////////////
|
||||
// State Configurations //
|
||||
//////////////////////////
|
||||
|
||||
// Use $stateProvider to configure your states.
|
||||
$stateProvider
|
||||
|
||||
//////////
|
||||
// Home //
|
||||
//////////
|
||||
|
||||
.state("home", {
|
||||
|
||||
// Use a url of "/" to set a states as the "index".
|
||||
url: "/",
|
||||
|
||||
// Example of an inline template string. By default, templates
|
||||
// will populate the ui-view within the parent state's template.
|
||||
// For top level states, like this one, the parent template is
|
||||
// the index.html file. So this template will be inserted into the
|
||||
// ui-view within index.html.
|
||||
template: '<p class="lead">Welcome to the UI-Router Demo</p>' +
|
||||
'<p>Use the menu above to navigate. ' +
|
||||
'Pay attention to the <code>$state</code> and <code>$stateParams</code> values below.</p>' +
|
||||
'<p>Click these links—<a href="#/c?id=1">Alice</a> or ' +
|
||||
'<a href="#/user/42">Bob</a>—to see a url redirect in action.</p>'
|
||||
|
||||
})
|
||||
|
||||
//////////////
|
||||
// Contacts //
|
||||
//////////////
|
||||
|
||||
.state('contacts', {
|
||||
|
||||
// With abstract set to true, that means this state can not be explicitly activated.
|
||||
// It can only be implicitly activated by activating one of it's children.
|
||||
abstract: true,
|
||||
|
||||
// This abstract state will prepend '/contacts' onto the urls of all its children.
|
||||
url: '/contacts',
|
||||
|
||||
// Example of loading a template from a file. This is also a top level state,
|
||||
// so this template file will be loaded and then inserted into the ui-view
|
||||
// within index.html.
|
||||
templateUrl: 'contacts.html',
|
||||
|
||||
// Use `resolve` to resolve any asynchronous controller dependencies
|
||||
// *before* the controller is instantiated. In this case, since contacts
|
||||
// returns a promise, the controller will wait until contacts.all() is
|
||||
// resolved before instantiation. Non-promise return values are considered
|
||||
// to be resolved immediately.
|
||||
resolve: {
|
||||
contacts: ['contacts',
|
||||
function( contacts){
|
||||
return contacts.all();
|
||||
}]
|
||||
},
|
||||
|
||||
// You can pair a controller to your template. There *must* be a template to pair with.
|
||||
controller: ['$scope', '$state', 'contacts', 'utils',
|
||||
function ( $scope, $state, contacts, utils) {
|
||||
|
||||
// Add a 'contacts' field in this abstract parent's scope, so that all
|
||||
// child state views can access it in their scopes. Please note: scope
|
||||
// inheritance is not due to nesting of states, but rather choosing to
|
||||
// nest the templates of those states. It's normal scope inheritance.
|
||||
$scope.contacts = contacts;
|
||||
|
||||
$scope.goToRandom = function () {
|
||||
var randId = utils.newRandomKey($scope.contacts, "id", $state.params.contactId);
|
||||
|
||||
// $state.go() can be used as a high level convenience method
|
||||
// for activating a state programmatically.
|
||||
$state.go('contacts.detail', { contactId: randId });
|
||||
};
|
||||
}]
|
||||
})
|
||||
|
||||
/////////////////////
|
||||
// Contacts > List //
|
||||
/////////////////////
|
||||
|
||||
// Using a '.' within a state name declares a child within a parent.
|
||||
// So you have a new state 'list' within the parent 'contacts' state.
|
||||
.state('contacts.list', {
|
||||
|
||||
// Using an empty url means that this child state will become active
|
||||
// when its parent's url is navigated to. Urls of child states are
|
||||
// automatically appended to the urls of their parent. So this state's
|
||||
// url is '/contacts' (because '/contacts' + '').
|
||||
url: '',
|
||||
|
||||
// IMPORTANT: Now we have a state that is not a top level state. Its
|
||||
// template will be inserted into the ui-view within this state's
|
||||
// parent's template; so the ui-view within contacts.html. This is the
|
||||
// most important thing to remember about templates.
|
||||
templateUrl: 'contacts.list.html'
|
||||
})
|
||||
|
||||
///////////////////////
|
||||
// Contacts > Detail //
|
||||
///////////////////////
|
||||
|
||||
// You can have unlimited children within a state. Here is a second child
|
||||
// state within the 'contacts' parent state.
|
||||
.state('contacts.detail', {
|
||||
|
||||
// Urls can have parameters. They can be specified like :param or {param}.
|
||||
// If {} is used, then you can also specify a regex pattern that the param
|
||||
// must match. The regex is written after a colon (:). Note: Don't use capture
|
||||
// groups in your regex patterns, because the whole regex is wrapped again
|
||||
// behind the scenes. Our pattern below will only match numbers with a length
|
||||
// between 1 and 4.
|
||||
|
||||
// Since this state is also a child of 'contacts' its url is appended as well.
|
||||
// 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}}',
|
||||
|
||||
// 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
|
||||
// views object to configure multiple views. Each view can get its own template,
|
||||
// controller, and resolve data.
|
||||
|
||||
// View names can be relative or absolute. Relative view names do not use an '@'
|
||||
// symbol. They always refer to views within this state's parent template.
|
||||
// Absolute view names use a '@' symbol to distinguish the view and the state.
|
||||
// So 'foo@bar' means the ui-view named 'foo' within the 'bar' state's template.
|
||||
views: {
|
||||
|
||||
// So this one is targeting the unnamed view within the parent state's template.
|
||||
'': {
|
||||
templateUrl: 'contacts.detail.html',
|
||||
controller: ['$scope', '$stateParams', 'utils',
|
||||
function ( $scope, $stateParams, utils) {
|
||||
$scope.contact = utils.findById($scope.contacts, $stateParams.contactId);
|
||||
}]
|
||||
},
|
||||
|
||||
// This one is targeting the ui-view="hint" within the unnamed root, aka index.html.
|
||||
// This shows off how you could populate *any* view within *any* ancestor state.
|
||||
'hint@': {
|
||||
template: 'This is contacts.detail populating the "hint" ui-view'
|
||||
},
|
||||
|
||||
// This one is targeting the ui-view="menu" within the parent state's template.
|
||||
'menuTip': {
|
||||
// templateProvider is the final method for supplying a template.
|
||||
// There is: template, templatUrl, and templateProvider.
|
||||
templateProvider: ['$stateParams',
|
||||
function ( $stateParams) {
|
||||
// This is just to demonstrate that $stateParams injection works for templateProvider.
|
||||
// $stateParams are the parameters for the new state we're transitioning to, even
|
||||
// though the global '$stateParams' has not been updated yet.
|
||||
return '<hr><small class="muted">Contact ID: ' + $stateParams.contactId + '</small>';
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//////////////////////////////
|
||||
// Contacts > Detail > Item //
|
||||
//////////////////////////////
|
||||
|
||||
.state('contacts.detail.item', {
|
||||
|
||||
// So following what we've learned, this state's full url will end up being
|
||||
// '/contacts/{contactId}/item/:itemId'. We are using both types of parameters
|
||||
// in the same url, but they behave identically.
|
||||
url: '/item/:itemId',
|
||||
views: {
|
||||
|
||||
// This is targeting the unnamed ui-view within the parent state 'contact.detail'
|
||||
// We wouldn't have to do it this way if we didn't also want to set the 'hint' view below.
|
||||
// We could instead just set templateUrl and controller outside of the view obj.
|
||||
'': {
|
||||
templateUrl: 'contacts.detail.item.html',
|
||||
controller: ['$scope', '$stateParams', '$state', 'utils',
|
||||
function ( $scope, $stateParams, $state, utils) {
|
||||
$scope.item = utils.findById($scope.contact.items, $stateParams.itemId);
|
||||
|
||||
$scope.edit = function () {
|
||||
// Here we show off go's ability to navigate to a relative state. Using '^' to go upwards
|
||||
// and '.' to go down, you can navigate to any relative state (ancestor or descendant).
|
||||
// Here we are going down to the child state 'edit' (full name of 'contacts.detail.item.edit')
|
||||
$state.go('.edit', $stateParams);
|
||||
};
|
||||
}]
|
||||
},
|
||||
|
||||
// Here we see we are overriding the template that was set by 'contact.detail'
|
||||
'hint@': {
|
||||
template: ' This is contacts.detail.item overriding the "hint" ui-view'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/////////////////////////////////////
|
||||
// Contacts > Detail > Item > Edit //
|
||||
/////////////////////////////////////
|
||||
|
||||
// Notice that this state has no 'url'. States do not require a url. You can use them
|
||||
// simply to organize your application into "places" where each "place" can configure
|
||||
// only what it needs. The only way to get to this state is via $state.go (or transitionTo)
|
||||
.state('contacts.detail.item.edit', {
|
||||
views: {
|
||||
|
||||
// This is targeting the unnamed view within the 'contact.detail' state
|
||||
// essentially swapping out the template that 'contact.detail.item' had
|
||||
// had inserted with this state's template.
|
||||
'@contacts.detail': {
|
||||
templateUrl: 'contacts.detail.item.edit.html',
|
||||
controller: ['$scope', '$stateParams', '$state', 'utils',
|
||||
function ( $scope, $stateParams, $state, utils) {
|
||||
$scope.item = utils.findById($scope.contact.items, $stateParams.itemId);
|
||||
$scope.done = function () {
|
||||
// Go back up. '^' means up one. '^.^' would be up twice, to the grandparent.
|
||||
$state.go('^', $stateParams);
|
||||
};
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
///////////
|
||||
// About //
|
||||
///////////
|
||||
|
||||
.state('about', {
|
||||
url: '/about',
|
||||
|
||||
// Showing off how you could return a promise from templateProvider
|
||||
templateProvider: ['$timeout',
|
||||
function ( $timeout) {
|
||||
return $timeout(function () {
|
||||
return '<p class="lead">UI-Router Resources</p><ul>' +
|
||||
'<li><a href="https://github.com/angular-ui/ui-router/tree/master/sample">Source for this Sample</a></li>' +
|
||||
'<li><a href="https://github.com/angular-ui/ui-router">Github Main Page</a></li>' +
|
||||
'<li><a href="https://github.com/angular-ui/ui-router#quick-start">Quick Start</a></li>' +
|
||||
'<li><a href="https://github.com/angular-ui/ui-router/wiki">In-Depth Guide</a></li>' +
|
||||
'<li><a href="https://github.com/angular-ui/ui-router/wiki/Quick-Reference">API Reference</a></li>' +
|
||||
'</ul>';
|
||||
}, 100);
|
||||
}]
|
||||
})
|
||||
}]);
|
||||
@@ -0,0 +1,11 @@
|
||||
.fade-enter-setup, .fade-leave-setup {
|
||||
transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0s;
|
||||
}
|
||||
.fade-enter-setup,
|
||||
.fade-leave-setup.fade-leave-start {
|
||||
opacity: 0;
|
||||
}
|
||||
.fade-leave-setup,
|
||||
.fade-enter-setup.fade-enter-start {
|
||||
opacity: 1;
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
/*jshint globalstrict:true*/
|
||||
/*global angular:false*/
|
||||
'use strict';
|
||||
|
||||
var isDefined = angular.isDefined,
|
||||
isFunction = angular.isFunction,
|
||||
isString = angular.isString,
|
||||
isObject = angular.isObject,
|
||||
isArray = angular.isArray,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy;
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, { prototype: parent }))(), extra);
|
||||
}
|
||||
|
||||
function merge(dst) {
|
||||
forEach(arguments, function(obj) {
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key) {
|
||||
if (!dst.hasOwnProperty(key)) dst[key] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the common ancestor path between two states.
|
||||
*
|
||||
* @param {Object} first The first state.
|
||||
* @param {Object} second The second state.
|
||||
* @return {Array} Returns an array of state names in descending order, not including the root.
|
||||
*/
|
||||
function ancestors(first, second) {
|
||||
var path = [];
|
||||
|
||||
for (var n in first.path) {
|
||||
if (first.path[n] === "") continue;
|
||||
if (!second.path[n]) break;
|
||||
path.push(first.path[n]);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Object.keys()`.
|
||||
*
|
||||
* @param {Object} object A JavaScript object.
|
||||
* @return {Array} Returns the keys of the object as an array.
|
||||
*/
|
||||
function keys(object) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(object);
|
||||
}
|
||||
var result = [];
|
||||
|
||||
angular.forEach(object, function(val, key) {
|
||||
result.push(key);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Array.prototype.indexOf()`.
|
||||
*
|
||||
* @param {Array} array A JavaScript array.
|
||||
* @param {*} value A value to search the array for.
|
||||
* @return {Number} Returns the array index value of `value`, or `-1` if not present.
|
||||
*/
|
||||
function arraySearch(array, value) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return array.indexOf(value, Number(arguments[2]) || 0);
|
||||
}
|
||||
var len = array.length >>> 0, from = Number(arguments[2]) || 0;
|
||||
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
|
||||
|
||||
if (from < 0) from += len;
|
||||
|
||||
for (; from < len; from++) {
|
||||
if (from in array && array[from] === value) return from;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a set of parameters with all parameters inherited between the common parents of the
|
||||
* current state and a given destination state.
|
||||
*
|
||||
* @param {Object} currentParams The value of the current state parameters ($stateParams).
|
||||
* @param {Object} newParams The set of parameters which will be composited with inherited params.
|
||||
* @param {Object} $current Internal definition of object representing the current state.
|
||||
* @param {Object} $to Internal definition of object representing state to transition to.
|
||||
*/
|
||||
function inheritParams(currentParams, newParams, $current, $to) {
|
||||
var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
|
||||
|
||||
for (var i in parents) {
|
||||
if (!parents[i].params || !parents[i].params.length) continue;
|
||||
parentParams = parents[i].params;
|
||||
|
||||
for (var j in parentParams) {
|
||||
if (arraySearch(inheritList, parentParams[j]) >= 0) continue;
|
||||
inheritList.push(parentParams[j]);
|
||||
inherited[parentParams[j]] = currentParams[parentParams[j]];
|
||||
}
|
||||
}
|
||||
return extend({}, inherited, newParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a set of values to string or `null`, filtering them by a list of keys.
|
||||
*
|
||||
* @param {Array} keys The list of keys to normalize/return.
|
||||
* @param {Object} values An object hash of values to normalize.
|
||||
* @return {Object} Returns an object hash of normalized string values.
|
||||
*/
|
||||
function normalize(keys, values) {
|
||||
var normalized = {};
|
||||
|
||||
forEach(keys, function (name) {
|
||||
var value = values[name];
|
||||
normalized[name] = (value != null) ? String(value) : null;
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
|
||||
*
|
||||
* @param {Object} a The first object.
|
||||
* @param {Object} b The second object.
|
||||
* @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
|
||||
* it defaults to the list of keys in `a`.
|
||||
* @return {Boolean} Returns `true` if the keys match, otherwise `false`.
|
||||
*/
|
||||
function equalForKeys(a, b, keys) {
|
||||
if (!keys) {
|
||||
keys = [];
|
||||
for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
|
||||
}
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var k = keys[i];
|
||||
if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of an object, based on a list of keys.
|
||||
*
|
||||
* @param {Array} keys
|
||||
* @param {Object} values
|
||||
* @return {Boolean} Returns a subset of `values`.
|
||||
*/
|
||||
function filterByKeys(keys, values) {
|
||||
var filtered = {};
|
||||
|
||||
forEach(keys, function (name) {
|
||||
filtered[name] = values[name];
|
||||
});
|
||||
return filtered;
|
||||
}
|
||||
|
||||
angular.module('ui.router.util', ['ng']);
|
||||
angular.module('ui.router.router', ['ui.router.util']);
|
||||
angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
|
||||
angular.module('ui.router', ['ui.router.state']);
|
||||
angular.module('ui.router.compat', ['ui.router']);
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
$RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider'];
|
||||
function $RouteProvider( $stateProvider, $urlRouterProvider) {
|
||||
|
||||
var routes = [];
|
||||
|
||||
onEnterRoute.$inject = ['$$state'];
|
||||
function onEnterRoute( $$state) {
|
||||
/*jshint validthis: true */
|
||||
this.locals = $$state.locals.globals;
|
||||
this.params = this.locals.$stateParams;
|
||||
}
|
||||
|
||||
function onExitRoute() {
|
||||
/*jshint validthis: true */
|
||||
this.locals = null;
|
||||
this.params = null;
|
||||
}
|
||||
|
||||
this.when = when;
|
||||
function when(url, route) {
|
||||
/*jshint validthis: true */
|
||||
if (route.redirectTo != null) {
|
||||
// Redirect, configure directly on $urlRouterProvider
|
||||
var redirect = route.redirectTo, handler;
|
||||
if (isString(redirect)) {
|
||||
handler = redirect; // leave $urlRouterProvider to handle
|
||||
} else if (isFunction(redirect)) {
|
||||
// Adapt to $urlRouterProvider API
|
||||
handler = function (params, $location) {
|
||||
return redirect(params, $location.path(), $location.search());
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid 'redirectTo' in when()");
|
||||
}
|
||||
$urlRouterProvider.when(url, handler);
|
||||
} else {
|
||||
// Regular route, configure as state
|
||||
$stateProvider.state(inherit(route, {
|
||||
parent: null,
|
||||
name: 'route:' + encodeURIComponent(url),
|
||||
url: url,
|
||||
onEnter: onEnterRoute,
|
||||
onExit: onExitRoute
|
||||
}));
|
||||
}
|
||||
routes.push(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$state', '$rootScope', '$routeParams'];
|
||||
function $get( $state, $rootScope, $routeParams) {
|
||||
|
||||
var $route = {
|
||||
routes: routes,
|
||||
params: $routeParams,
|
||||
current: undefined
|
||||
};
|
||||
|
||||
function stateAsRoute(state) {
|
||||
return (state.name !== '') ? state : undefined;
|
||||
}
|
||||
|
||||
$rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
|
||||
$rootScope.$broadcast('$routeChangeStart', stateAsRoute(to), stateAsRoute(from));
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
|
||||
$route.current = stateAsRoute(to);
|
||||
$rootScope.$broadcast('$routeChangeSuccess', stateAsRoute(to), stateAsRoute(from));
|
||||
copy(toParams, $route.params);
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeError', function (ev, to, toParams, from, fromParams, error) {
|
||||
$rootScope.$broadcast('$routeChangeError', stateAsRoute(to), stateAsRoute(from), error);
|
||||
});
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.router.compat')
|
||||
.provider('$route', $RouteProvider)
|
||||
.directive('ngView', $ViewDirective);
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Service (`ui-util`). Manages resolution of (acyclic) graphs of promises.
|
||||
* @module $resolve
|
||||
* @requires $q
|
||||
* @requires $injector
|
||||
*/
|
||||
$Resolve.$inject = ['$q', '$injector'];
|
||||
function $Resolve( $q, $injector) {
|
||||
|
||||
var VISIT_IN_PROGRESS = 1,
|
||||
VISIT_DONE = 2,
|
||||
NOTHING = {},
|
||||
NO_DEPENDENCIES = [],
|
||||
NO_LOCALS = NOTHING,
|
||||
NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
|
||||
|
||||
|
||||
/**
|
||||
* Studies a set of invocables that are likely to be used multiple times.
|
||||
* $resolve.study(invocables)(locals, parent, self)
|
||||
* is equivalent to
|
||||
* $resolve.resolve(invocables, locals, parent, self)
|
||||
* but the former is more efficient (in fact `resolve` just calls `study` internally).
|
||||
* See {@link module:$resolve/resolve} for details.
|
||||
* @function
|
||||
* @param {Object} invocables
|
||||
* @return {Function}
|
||||
*/
|
||||
this.study = function (invocables) {
|
||||
if (!isObject(invocables)) throw new Error("'invocables' must be an object");
|
||||
|
||||
// Perform a topological sort of invocables to build an ordered plan
|
||||
var plan = [], cycle = [], visited = {};
|
||||
function visit(value, key) {
|
||||
if (visited[key] === VISIT_DONE) return;
|
||||
|
||||
cycle.push(key);
|
||||
if (visited[key] === VISIT_IN_PROGRESS) {
|
||||
cycle.splice(0, cycle.indexOf(key));
|
||||
throw new Error("Cyclic dependency: " + cycle.join(" -> "));
|
||||
}
|
||||
visited[key] = VISIT_IN_PROGRESS;
|
||||
|
||||
if (isString(value)) {
|
||||
plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
|
||||
} else {
|
||||
var params = $injector.annotate(value);
|
||||
forEach(params, function (param) {
|
||||
if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
|
||||
});
|
||||
plan.push(key, value, params);
|
||||
}
|
||||
|
||||
cycle.pop();
|
||||
visited[key] = VISIT_DONE;
|
||||
}
|
||||
forEach(invocables, visit);
|
||||
invocables = cycle = visited = null; // plan is all that's required
|
||||
|
||||
function isResolve(value) {
|
||||
return isObject(value) && value.then && value.$$promises;
|
||||
}
|
||||
|
||||
return function (locals, parent, self) {
|
||||
if (isResolve(locals) && self === undefined) {
|
||||
self = parent; parent = locals; locals = null;
|
||||
}
|
||||
if (!locals) locals = NO_LOCALS;
|
||||
else if (!isObject(locals)) {
|
||||
throw new Error("'locals' must be an object");
|
||||
}
|
||||
if (!parent) parent = NO_PARENT;
|
||||
else if (!isResolve(parent)) {
|
||||
throw new Error("'parent' must be a promise returned by $resolve.resolve()");
|
||||
}
|
||||
|
||||
// To complete the overall resolution, we have to wait for the parent
|
||||
// promise and for the promise for each invokable in our plan.
|
||||
var resolution = $q.defer(),
|
||||
result = resolution.promise,
|
||||
promises = result.$$promises = {},
|
||||
values = extend({}, locals),
|
||||
wait = 1 + plan.length/3,
|
||||
merged = false;
|
||||
|
||||
function done() {
|
||||
// Merge parent values we haven't got yet and publish our own $$values
|
||||
if (!--wait) {
|
||||
if (!merged) merge(values, parent.$$values);
|
||||
result.$$values = values;
|
||||
result.$$promises = true; // keep for isResolve()
|
||||
resolution.resolve(values);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(reason) {
|
||||
result.$$failure = reason;
|
||||
resolution.reject(reason);
|
||||
}
|
||||
|
||||
// Short-circuit if parent has already failed
|
||||
if (isDefined(parent.$$failure)) {
|
||||
fail(parent.$$failure);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Merge parent values if the parent has already resolved, or merge
|
||||
// parent promises and wait if the parent resolve is still in progress.
|
||||
if (parent.$$values) {
|
||||
merged = merge(values, parent.$$values);
|
||||
done();
|
||||
} else {
|
||||
extend(promises, parent.$$promises);
|
||||
parent.then(done, fail);
|
||||
}
|
||||
|
||||
// Process each invocable in the plan, but ignore any where a local of the same name exists.
|
||||
for (var i=0, ii=plan.length; i<ii; i+=3) {
|
||||
if (locals.hasOwnProperty(plan[i])) done();
|
||||
else invoke(plan[i], plan[i+1], plan[i+2]);
|
||||
}
|
||||
|
||||
function invoke(key, invocable, params) {
|
||||
// Create a deferred for this invocation. Failures will propagate to the resolution as well.
|
||||
var invocation = $q.defer(), waitParams = 0;
|
||||
function onfailure(reason) {
|
||||
invocation.reject(reason);
|
||||
fail(reason);
|
||||
}
|
||||
// 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).
|
||||
forEach(params, function (dep) {
|
||||
if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
|
||||
waitParams++;
|
||||
promises[dep].then(function (result) {
|
||||
values[dep] = result;
|
||||
if (!(--waitParams)) proceed();
|
||||
}, onfailure);
|
||||
}
|
||||
});
|
||||
if (!waitParams) proceed();
|
||||
function proceed() {
|
||||
if (isDefined(result.$$failure)) return;
|
||||
try {
|
||||
invocation.resolve($injector.invoke(invocable, self, values));
|
||||
invocation.promise.then(function (result) {
|
||||
values[key] = result;
|
||||
done();
|
||||
}, onfailure);
|
||||
} catch (e) {
|
||||
onfailure(e);
|
||||
}
|
||||
}
|
||||
// Publish promise synchronously; invocations further down in the plan may depend on it.
|
||||
promises[key] = invocation.promise;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves a set of invocables. An invocable is a function to be invoked via `$injector.invoke()`,
|
||||
* and can have an arbitrary number of dependencies. An invocable can either return a value directly,
|
||||
* or a `$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.
|
||||
*
|
||||
* @function
|
||||
* @param {Object.<string, Function|string>} invocables functions to invoke or `$injector` services to fetch.
|
||||
* @param {Object.<string, *>} [locals] values to make available to the injectables
|
||||
* @param {Promise.<Object>} [parent] a promise returned by another call to `$resolve`.
|
||||
* @param {Object} [self] the `this` for the invoked methods
|
||||
* @return {Promise.<Object>} Promise for an object that contains the resolved return value
|
||||
* of all invocables, as well as any inherited and local values.
|
||||
*/
|
||||
this.resolve = function (invocables, locals, parent, self) {
|
||||
return this.study(invocables)(locals, parent, self);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$resolve', $Resolve);
|
||||
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
$StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider', '$locationProvider'];
|
||||
function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $locationProvider) {
|
||||
|
||||
var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
|
||||
|
||||
// Builds state properties from definition passed to registerState()
|
||||
var stateBuilder = {
|
||||
|
||||
// Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
|
||||
// state.children = [];
|
||||
// if (parent) parent.children.push(state);
|
||||
parent: function(state) {
|
||||
if (isDefined(state.parent) && state.parent) return findState(state.parent);
|
||||
// regex matches any valid composite state name
|
||||
// would match "contact.list" but not "contacts"
|
||||
var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
|
||||
return compositeName ? findState(compositeName[1]) : root;
|
||||
},
|
||||
|
||||
// inherit 'data' from parent and override by own values (if any)
|
||||
data: function(state) {
|
||||
if (state.parent && state.parent.data) {
|
||||
state.data = state.self.data = extend({}, state.parent.data, state.data);
|
||||
}
|
||||
return state.data;
|
||||
},
|
||||
|
||||
// Build a URLMatcher if necessary, either via a relative or absolute URL
|
||||
url: function(state) {
|
||||
var url = state.url;
|
||||
|
||||
if (isString(url)) {
|
||||
if (url.charAt(0) == '^') {
|
||||
return $urlMatcherFactory.compile(url.substring(1));
|
||||
}
|
||||
return (state.parent.navigable || root).url.concat(url);
|
||||
}
|
||||
|
||||
if ($urlMatcherFactory.isMatcher(url) || url == null) {
|
||||
return url;
|
||||
}
|
||||
throw new Error("Invalid url '" + url + "' in state '" + state + "'");
|
||||
},
|
||||
|
||||
// Keep track of the closest ancestor state that has a URL (i.e. is navigable)
|
||||
navigable: function(state) {
|
||||
return state.url ? state : (state.parent ? state.parent.navigable : null);
|
||||
},
|
||||
|
||||
// Derive parameters for this state and ensure they're a super-set of parent's parameters
|
||||
params: function(state) {
|
||||
if (!state.params) {
|
||||
return state.url ? state.url.parameters() : state.parent.params;
|
||||
}
|
||||
if (!isArray(state.params)) throw new Error("Invalid params in state '" + state + "'");
|
||||
if (state.url) throw new Error("Both params and url specicified in state '" + state + "'");
|
||||
return state.params;
|
||||
},
|
||||
|
||||
// If there is no explicit multi-view configuration, make one up so we don't have
|
||||
// to handle both cases in the view directive later. Note that having an explicit
|
||||
// 'views' property will mean the default unnamed view properties are ignored. This
|
||||
// is also a good time to resolve view names to absolute names, so everything is a
|
||||
// straight lookup at link time.
|
||||
views: function(state) {
|
||||
var views = {};
|
||||
|
||||
forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
|
||||
if (name.indexOf('@') < 0) name += '@' + state.parent.name;
|
||||
views[name] = view;
|
||||
});
|
||||
return views;
|
||||
},
|
||||
|
||||
ownParams: function(state) {
|
||||
if (!state.parent) {
|
||||
return state.params;
|
||||
}
|
||||
var paramNames = {}; forEach(state.params, function (p) { paramNames[p] = true; });
|
||||
|
||||
forEach(state.parent.params, function (p) {
|
||||
if (!paramNames[p]) {
|
||||
throw new Error("Missing required parameter '" + p + "' in state '" + state.name + "'");
|
||||
}
|
||||
paramNames[p] = false;
|
||||
});
|
||||
var ownParams = [];
|
||||
|
||||
forEach(paramNames, function (own, p) {
|
||||
if (own) ownParams.push(p);
|
||||
});
|
||||
return ownParams;
|
||||
},
|
||||
|
||||
// Keep a full path from the root down to this state as this is needed for state activation.
|
||||
path: function(state) {
|
||||
return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
|
||||
},
|
||||
|
||||
// Speed up $state.contains() as it's used a lot
|
||||
includes: function(state) {
|
||||
var includes = state.parent ? extend({}, state.parent.includes) : {};
|
||||
includes[state.name] = true;
|
||||
return includes;
|
||||
},
|
||||
|
||||
$delegates: {}
|
||||
};
|
||||
|
||||
function isRelative(stateName) {
|
||||
return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
|
||||
}
|
||||
|
||||
function findState(stateOrName, base) {
|
||||
var isStr = isString(stateOrName),
|
||||
name = isStr ? stateOrName : stateOrName.name,
|
||||
path = isRelative(name);
|
||||
|
||||
if (path) {
|
||||
if (!base) throw new Error("No reference point given for path '" + name + "'");
|
||||
var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
|
||||
|
||||
for (; i < pathLength; i++) {
|
||||
if (rel[i] === "" && i === 0) {
|
||||
current = base;
|
||||
continue;
|
||||
}
|
||||
if (rel[i] === "^") {
|
||||
if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
|
||||
current = current.parent;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rel = rel.slice(i).join(".");
|
||||
name = current.name + (current.name && rel ? "." : "") + rel;
|
||||
}
|
||||
var state = states[name];
|
||||
|
||||
if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
|
||||
return state;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function queueState(parentName, state) {
|
||||
if (!queue[parentName]) {
|
||||
queue[parentName] = [];
|
||||
}
|
||||
queue[parentName].push(state);
|
||||
}
|
||||
|
||||
function registerState(state) {
|
||||
// Wrap a new object around the state so we can store our private details easily.
|
||||
state = inherit(state, {
|
||||
self: state,
|
||||
resolve: state.resolve || {},
|
||||
toString: function() { return this.name; }
|
||||
});
|
||||
|
||||
var name = state.name;
|
||||
if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
|
||||
if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
|
||||
|
||||
// Get parent name
|
||||
var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
|
||||
: (isString(state.parent)) ? state.parent
|
||||
: '';
|
||||
|
||||
// If parent is not registered yet, add state to queue and register later
|
||||
if (parentName && !states[parentName]) {
|
||||
return queueState(parentName, state.self);
|
||||
}
|
||||
|
||||
for (var key in stateBuilder) {
|
||||
if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
|
||||
}
|
||||
states[name] = state;
|
||||
|
||||
// Register the state in the global state list and with $urlRouter if necessary.
|
||||
if (!state[abstractKey] && state.url) {
|
||||
$urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
|
||||
if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
|
||||
$state.transitionTo(state, $match, { location: false });
|
||||
}
|
||||
}]);
|
||||
}
|
||||
|
||||
// Register any queued children
|
||||
if (queue[name]) {
|
||||
for (var i = 0; i < queue[name].length; i++) {
|
||||
registerState(queue[name][i]);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
// Implicit root state that is always active
|
||||
root = registerState({
|
||||
name: '',
|
||||
url: '^',
|
||||
views: null,
|
||||
'abstract': true
|
||||
});
|
||||
root.navigable = null;
|
||||
|
||||
|
||||
// .decorator()
|
||||
// .decorator(name)
|
||||
// .decorator(name, function)
|
||||
this.decorator = decorator;
|
||||
function decorator(name, func) {
|
||||
/*jshint validthis: true */
|
||||
if (isString(name) && !isDefined(func)) {
|
||||
return stateBuilder[name];
|
||||
}
|
||||
if (!isFunction(func) || !isString(name)) {
|
||||
return this;
|
||||
}
|
||||
if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
|
||||
stateBuilder.$delegates[name] = stateBuilder[name];
|
||||
}
|
||||
stateBuilder[name] = func;
|
||||
return this;
|
||||
}
|
||||
|
||||
// .state(state)
|
||||
// .state(name, state)
|
||||
this.state = state;
|
||||
function state(name, definition) {
|
||||
/*jshint validthis: true */
|
||||
if (isObject(name)) definition = name;
|
||||
else definition.name = name;
|
||||
registerState(definition);
|
||||
return this;
|
||||
}
|
||||
|
||||
// $urlRouter is injected just to ensure it gets instantiated
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$location', '$urlRouter'];
|
||||
function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $location, $urlRouter) {
|
||||
|
||||
var TransitionSuperseded = $q.reject(new Error('transition superseded'));
|
||||
var TransitionPrevented = $q.reject(new Error('transition prevented'));
|
||||
var TransitionAborted = $q.reject(new Error('transition aborted'));
|
||||
var TransitionFailed = $q.reject(new Error('transition failed'));
|
||||
var currentLocation = $location.url();
|
||||
|
||||
function syncUrl() {
|
||||
if ($location.url() !== currentLocation) {
|
||||
$location.url(currentLocation);
|
||||
$location.replace();
|
||||
}
|
||||
}
|
||||
|
||||
root.locals = { resolve: null, globals: { $stateParams: {} } };
|
||||
$state = {
|
||||
params: {},
|
||||
current: root.self,
|
||||
$current: root,
|
||||
transition: null
|
||||
};
|
||||
|
||||
$state.reload = function reload() {
|
||||
$state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false });
|
||||
};
|
||||
|
||||
$state.go = function go(to, params, options) {
|
||||
return this.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
|
||||
};
|
||||
|
||||
$state.transitionTo = function transitionTo(to, toParams, options) {
|
||||
toParams = toParams || {};
|
||||
options = extend({
|
||||
location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
|
||||
}, options || {});
|
||||
|
||||
var from = $state.$current, fromParams = $state.params, fromPath = from.path;
|
||||
var evt, toState = findState(to, options.relative);
|
||||
|
||||
if (!isDefined(toState)) {
|
||||
// Broadcast not found event and abort the transition if prevented
|
||||
var redirect = { to: to, toParams: toParams, options: options };
|
||||
evt = $rootScope.$broadcast('$stateNotFound', redirect, from.self, fromParams);
|
||||
if (evt.defaultPrevented) {
|
||||
syncUrl();
|
||||
return TransitionAborted;
|
||||
}
|
||||
|
||||
// Allow the handler to return a promise to defer state lookup retry
|
||||
if (evt.retry) {
|
||||
if (options.$retry) {
|
||||
syncUrl();
|
||||
return TransitionFailed;
|
||||
}
|
||||
var retryTransition = $state.transition = $q.when(evt.retry);
|
||||
retryTransition.then(function() {
|
||||
if (retryTransition !== $state.transition) return TransitionSuperseded;
|
||||
redirect.options.$retry = true;
|
||||
return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
|
||||
}, function() {
|
||||
return TransitionAborted;
|
||||
});
|
||||
syncUrl();
|
||||
return retryTransition;
|
||||
}
|
||||
|
||||
// Always retry once if the $stateNotFound was not prevented
|
||||
// (handles either redirect changed or state lazy-definition)
|
||||
to = redirect.to;
|
||||
toParams = redirect.toParams;
|
||||
options = redirect.options;
|
||||
toState = findState(to, options.relative);
|
||||
if (!isDefined(toState)) {
|
||||
if (options.relative) throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
|
||||
throw new Error("No such state '" + to + "'");
|
||||
}
|
||||
}
|
||||
if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
|
||||
if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
|
||||
to = toState;
|
||||
|
||||
var toPath = to.path;
|
||||
|
||||
// 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) && !options.reload;
|
||||
keep++, state = toPath[keep]) {
|
||||
locals = toLocals[keep] = state.locals;
|
||||
}
|
||||
|
||||
// If we're going to the same state and all locals are kept, we've got nothing to do.
|
||||
// But clear 'transition', as we still want to cancel any other pending transitions.
|
||||
// TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
|
||||
// because we might accidentally abort a legitimate transition initiated from code?
|
||||
if (shouldTriggerReload(to, from, locals, options) ) {
|
||||
if ( to.self.reloadOnSearch !== false )
|
||||
syncUrl();
|
||||
$state.transition = null;
|
||||
return $q.when($state.current);
|
||||
}
|
||||
|
||||
// Normalize/filter parameters before we pass them to event handlers etc.
|
||||
toParams = normalize(to.params, toParams || {});
|
||||
|
||||
// Broadcast start event and cancel the transition if requested
|
||||
if (options.notify) {
|
||||
evt = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);
|
||||
if (evt.defaultPrevented) {
|
||||
syncUrl();
|
||||
return TransitionPrevented;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve locals for the remaining states, but don't update any global state just
|
||||
// yet -- if anything fails to resolve the current state needs to remain untouched.
|
||||
// We also set up an inheritance chain for the locals here. This allows the view directive
|
||||
// to quickly look up the correct definition for each view in the current state. Even
|
||||
// though we create the locals object itself outside resolveState(), it is initially
|
||||
// empty and gets filled asynchronously. We need to keep track of the promise for the
|
||||
// (fully resolved) current locals, and pass this down the chain.
|
||||
var resolved = $q.when(locals);
|
||||
for (var l=keep; l<toPath.length; l++, state=toPath[l]) {
|
||||
locals = toLocals[l] = inherit(locals);
|
||||
resolved = resolveState(state, toParams, state===to, resolved, locals);
|
||||
}
|
||||
|
||||
// Once everything is resolved, we are ready to perform the actual transition
|
||||
// and return a promise for the new state. We also keep track of what the
|
||||
// current promise is, so that we can detect overlapping transitions and
|
||||
// keep only the outcome of the last transition.
|
||||
var transition = $state.transition = resolved.then(function () {
|
||||
var l, entering, exiting;
|
||||
|
||||
if ($state.transition !== transition) return TransitionSuperseded;
|
||||
|
||||
// Exit 'from' states not kept
|
||||
for (l=fromPath.length-1; l>=keep; l--) {
|
||||
exiting = fromPath[l];
|
||||
if (exiting.self.onExit) {
|
||||
$injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
|
||||
}
|
||||
exiting.locals = null;
|
||||
}
|
||||
|
||||
// Enter 'to' states not kept
|
||||
for (l=keep; l<toPath.length; l++) {
|
||||
entering = toPath[l];
|
||||
entering.locals = toLocals[l];
|
||||
if (entering.self.onEnter) {
|
||||
$injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
|
||||
}
|
||||
}
|
||||
|
||||
// Run it again, to catch any transitions in callbacks
|
||||
if ($state.transition !== transition) return TransitionSuperseded;
|
||||
|
||||
// Update globals in $state
|
||||
$state.$current = to;
|
||||
$state.current = to.self;
|
||||
$state.params = toParams;
|
||||
copy($state.params, $stateParams);
|
||||
$state.transition = null;
|
||||
|
||||
// Update $location
|
||||
var toNav = to.navigable;
|
||||
if (options.location && toNav) {
|
||||
$location.url(toNav.url.format(toNav.locals.globals.$stateParams));
|
||||
|
||||
if (options.location === 'replace') {
|
||||
$location.replace();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.notify) {
|
||||
$rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
|
||||
}
|
||||
currentLocation = $location.url();
|
||||
|
||||
return $state.current;
|
||||
}, function (error) {
|
||||
if ($state.transition !== transition) return TransitionSuperseded;
|
||||
|
||||
$state.transition = null;
|
||||
$rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
|
||||
syncUrl();
|
||||
|
||||
return $q.reject(error);
|
||||
});
|
||||
|
||||
return transition;
|
||||
};
|
||||
|
||||
$state.is = function is(stateOrName, params) {
|
||||
var state = findState(stateOrName);
|
||||
|
||||
if (!isDefined(state)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ($state.$current !== state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isDefined(params) ? angular.equals($stateParams, params) : true;
|
||||
};
|
||||
|
||||
$state.includes = function includes(stateOrName, params) {
|
||||
var state = findState(stateOrName);
|
||||
if (!isDefined(state)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!isDefined($state.$current.includes[state.name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var validParams = true;
|
||||
angular.forEach(params, function(value, key) {
|
||||
if (!isDefined($stateParams[key]) || $stateParams[key] !== value) {
|
||||
validParams = false;
|
||||
}
|
||||
});
|
||||
return validParams;
|
||||
};
|
||||
|
||||
$state.href = function href(stateOrName, params, options) {
|
||||
options = extend({ lossy: true, inherit: false, absolute: false, relative: $state.$current }, options || {});
|
||||
var state = findState(stateOrName, options.relative);
|
||||
if (!isDefined(state)) return null;
|
||||
|
||||
params = inheritParams($stateParams, params || {}, $state.$current, state);
|
||||
var nav = (state && options.lossy) ? state.navigable : state;
|
||||
var url = (nav && nav.url) ? nav.url.format(normalize(state.params, params || {})) : null;
|
||||
if (!$locationProvider.html5Mode() && url) {
|
||||
url = "#" + $locationProvider.hashPrefix() + url;
|
||||
}
|
||||
if (options.absolute && url) {
|
||||
url = $location.protocol() + '://' +
|
||||
$location.host() +
|
||||
($location.port() == 80 || $location.port() == 443 ? '' : ':' + $location.port()) +
|
||||
(!$locationProvider.html5Mode() && url ? '/' : '') +
|
||||
url;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
$state.get = function (stateOrName, context) {
|
||||
if (!isDefined(stateOrName)) {
|
||||
var list = [];
|
||||
forEach(states, function(state) { list.push(state.self); });
|
||||
return list;
|
||||
}
|
||||
var state = findState(stateOrName, context);
|
||||
return (state && state.self) ? state.self : null;
|
||||
};
|
||||
|
||||
function resolveState(state, params, paramsAreFiltered, inherited, dst) {
|
||||
// Make a restricted $stateParams with only the parameters that apply to this state if
|
||||
// necessary. In addition to being available to the controller and onEnter/onExit callbacks,
|
||||
// we also need $stateParams to be available for any $injector calls we make during the
|
||||
// dependency resolution process.
|
||||
var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params, params);
|
||||
var locals = { $stateParams: $stateParams };
|
||||
|
||||
// Resolve 'global' dependencies for the state, i.e. those not specific to a view.
|
||||
// We're also including $stateParams in this; that way the parameters are restricted
|
||||
// to the set that should be visible to the state, and are independent of when we update
|
||||
// the global $state and $stateParams values.
|
||||
dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
|
||||
var promises = [ dst.resolve.then(function (globals) {
|
||||
dst.globals = globals;
|
||||
}) ];
|
||||
if (inherited) promises.push(inherited);
|
||||
|
||||
// Resolve template and dependencies for all views.
|
||||
forEach(state.views, function (view, name) {
|
||||
var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
|
||||
injectables.$template = [ function () {
|
||||
return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: false }) || '';
|
||||
}];
|
||||
|
||||
promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) {
|
||||
// References to the controller (only instantiated at link time)
|
||||
if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
|
||||
var injectLocals = angular.extend({}, injectables, locals);
|
||||
result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
|
||||
} else {
|
||||
result.$$controller = view.controller;
|
||||
}
|
||||
// Provide access to the state itself for internal use
|
||||
result.$$state = state;
|
||||
dst[name] = result;
|
||||
}));
|
||||
});
|
||||
|
||||
// Wait for all the promises and then return the activation object
|
||||
return $q.all(promises).then(function (values) {
|
||||
return dst;
|
||||
});
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
function shouldTriggerReload(to, from, locals, options) {
|
||||
if ( to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false)) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.router.state')
|
||||
.value('$stateParams', {})
|
||||
.provider('$state', $StateProvider);
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
function parseStateRef(ref) {
|
||||
var parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
|
||||
if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
|
||||
return { state: parsed[1], paramExpr: parsed[3] || null };
|
||||
}
|
||||
|
||||
function stateContext(el) {
|
||||
var stateData = el.parent().inheritedData('$uiView');
|
||||
|
||||
if (stateData && stateData.state && stateData.state.name) {
|
||||
return stateData.state;
|
||||
}
|
||||
}
|
||||
|
||||
$StateRefDirective.$inject = ['$state', '$timeout'];
|
||||
function $StateRefDirective($state, $timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?^uiSrefActive',
|
||||
link: function(scope, element, attrs, uiSrefActive) {
|
||||
var ref = parseStateRef(attrs.uiSref);
|
||||
var params = null, url = null, base = stateContext(element) || $state.$current;
|
||||
var isForm = element[0].nodeName === "FORM";
|
||||
var attr = isForm ? "action" : "href", nav = true;
|
||||
|
||||
var update = function(newVal) {
|
||||
if (newVal) params = newVal;
|
||||
if (!nav) return;
|
||||
|
||||
var newHref = $state.href(ref.state, params, { relative: base });
|
||||
|
||||
if (!newHref) {
|
||||
nav = false;
|
||||
return false;
|
||||
}
|
||||
element[0][attr] = newHref;
|
||||
if (uiSrefActive) {
|
||||
uiSrefActive.$$setStateInfo(ref.state, params);
|
||||
}
|
||||
};
|
||||
|
||||
if (ref.paramExpr) {
|
||||
scope.$watch(ref.paramExpr, function(newVal, oldVal) {
|
||||
if (newVal !== params) update(newVal);
|
||||
}, true);
|
||||
params = scope.$eval(ref.paramExpr);
|
||||
}
|
||||
update();
|
||||
|
||||
if (isForm) return;
|
||||
|
||||
element.bind("click", function(e) {
|
||||
var button = e.which || e.button;
|
||||
|
||||
if ((button === 0 || button == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
||||
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
|
||||
$timeout(function() {
|
||||
scope.$apply(function() {
|
||||
$state.go(ref.state, params, { relative: base });
|
||||
});
|
||||
});
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$StateActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
|
||||
function $StateActiveDirective($state, $stateParams, $interpolate) {
|
||||
return {
|
||||
restrict: "A",
|
||||
controller: function($scope, $element, $attrs) {
|
||||
var state, params, activeClass;
|
||||
|
||||
// There probably isn't much point in $observing this
|
||||
activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope);
|
||||
|
||||
// Allow uiSref to communicate with uiSrefActive
|
||||
this.$$setStateInfo = function(newState, newParams) {
|
||||
state = $state.get(newState, stateContext($element));
|
||||
params = newParams;
|
||||
update();
|
||||
};
|
||||
|
||||
$scope.$on('$stateChangeSuccess', update);
|
||||
|
||||
// Update route state
|
||||
function update() {
|
||||
if ($state.$current.self === state && matchesParams()) {
|
||||
$element.addClass(activeClass);
|
||||
} else {
|
||||
$element.removeClass(activeClass);
|
||||
}
|
||||
}
|
||||
|
||||
function matchesParams() {
|
||||
return !params || equalForKeys(params, $stateParams);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.state')
|
||||
.directive('uiSref', $StateRefDirective)
|
||||
.directive('uiSrefActive', $StateActiveDirective);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
$IsStateFilter.$inject = ['$state'];
|
||||
function $IsStateFilter($state) {
|
||||
return function(state) {
|
||||
return $state.is(state);
|
||||
};
|
||||
}
|
||||
|
||||
$IncludedByStateFilter.$inject = ['$state'];
|
||||
function $IncludedByStateFilter($state) {
|
||||
return function(state) {
|
||||
return $state.includes(state);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.state')
|
||||
.filter('isState', $IsStateFilter)
|
||||
.filter('includedByState', $IncludedByStateFilter);
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Service. Manages loading of templates.
|
||||
* @constructor
|
||||
* @name $templateFactory
|
||||
* @requires $http
|
||||
* @requires $templateCache
|
||||
* @requires $injector
|
||||
*/
|
||||
$TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
|
||||
function $TemplateFactory( $http, $templateCache, $injector) {
|
||||
|
||||
/**
|
||||
* Creates a template from a configuration object.
|
||||
* @function
|
||||
* @name $templateFactory#fromConfig
|
||||
* @methodOf $templateFactory
|
||||
* @param {Object} config Configuration object for which to load a template. The following
|
||||
* properties are search in the specified order, and the first one that is defined is
|
||||
* used to create the template:
|
||||
* @param {string|Function} config.template html string template or function to load via
|
||||
* {@link $templateFactory#fromString fromString}.
|
||||
* @param {string|Function} config.templateUrl url to load or a function returning the url
|
||||
* to load via {@link $templateFactory#fromUrl fromUrl}.
|
||||
* @param {Function} config.templateProvider function to invoke via
|
||||
* {@link $templateFactory#fromProvider fromProvider}.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @param {Object} [locals] Locals to pass to `invoke` if the template is loaded via a
|
||||
* `templateProvider`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string,
|
||||
* or `null` if no template is configured.
|
||||
*/
|
||||
this.fromConfig = function (config, params, locals) {
|
||||
return (
|
||||
isDefined(config.template) ? this.fromString(config.template, params) :
|
||||
isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
|
||||
isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template from a string or a function returning a string.
|
||||
* @function
|
||||
* @name $templateFactory#fromString
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} template html template as a string or function that returns an html
|
||||
* template as a string.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromString = function (template, params) {
|
||||
return isFunction(template) ? template(params) : template;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a template from the a URL via `$http` and `$templateCache`.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} url url of the template to load, or a function that returns a url.
|
||||
* @param {Object} params Parameters to pass to the url function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromUrl = function (url, params) {
|
||||
if (isFunction(url)) url = url(params);
|
||||
if (url == null) return null;
|
||||
else return $http
|
||||
.get(url, { cache: $templateCache })
|
||||
.then(function(response) { return response.data; });
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template by invoking an injectable provider function.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {Function} provider Function to invoke via `$injector.invoke`
|
||||
* @param {Object} params Parameters for the template.
|
||||
* @param {Object} [locals] Locals to pass to `invoke`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromProvider = function (provider, params, locals) {
|
||||
return $injector.invoke(provider, null, locals || { params: params });
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Matches URLs against patterns and extracts named parameters from the path or the search
|
||||
* part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
|
||||
* of search parameters. Multiple search parameter names are separated by '&'. Search parameters
|
||||
* do not influence whether or not a URL is matched, but their values are passed through into
|
||||
* the matched parameters returned by {@link UrlMatcher#exec exec}.
|
||||
*
|
||||
* Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
|
||||
* syntax, which optionally allows a regular expression for the parameter to be specified:
|
||||
*
|
||||
* * ':' name - colon placeholder
|
||||
* * '*' name - catch-all placeholder
|
||||
* * '{' name '}' - curly placeholder
|
||||
* * '{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain
|
||||
* curly braces, they must be in matched pairs or escaped with a backslash.
|
||||
*
|
||||
* Parameter names may contain only word characters (latin letters, digits, and underscore) and
|
||||
* must be unique within the pattern (across both path and search parameters). For colon
|
||||
* placeholders or curly placeholders without an explicit regexp, a path parameter matches any
|
||||
* number of characters other than '/'. For catch-all placeholders the path parameter matches
|
||||
* any number of characters.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* * '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for
|
||||
* trailing slashes, and patterns have to match the entire path, not just a prefix.
|
||||
* * '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
|
||||
* '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
|
||||
* * '/user/{id}' - Same as the previous example, but using curly brace syntax.
|
||||
* * '/user/{id:[^/]*}' - Same as the previous example.
|
||||
* * '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id
|
||||
* parameter consists of 1 to 8 hex digits.
|
||||
* * '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the
|
||||
* path into the parameter 'path'.
|
||||
* * '/files/*path' - ditto.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} pattern the pattern to compile into a matcher.
|
||||
*
|
||||
* @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
|
||||
* URL matching this matcher (i.e. any string for which {@link UrlMatcher#exec exec()} returns
|
||||
* non-null) will start with this prefix.
|
||||
*/
|
||||
function UrlMatcher(pattern) {
|
||||
|
||||
// Find all placeholders and create a compiled pattern, using either classic or curly syntax:
|
||||
// '*' name
|
||||
// ':' name
|
||||
// '{' name '}'
|
||||
// '{' name ':' regexp '}'
|
||||
// The regular expression is somewhat complicated due to the need to allow curly braces
|
||||
// inside the regular expression. The placeholder regexp breaks down as follows:
|
||||
// ([:*])(\w+) classic placeholder ($1 / $2)
|
||||
// \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
|
||||
// (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
|
||||
// [^{}\\]+ - anything other than curly braces or backslash
|
||||
// \\. - a backslash escape
|
||||
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
|
||||
var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
|
||||
names = {}, compiled = '^', last = 0, m,
|
||||
segments = this.segments = [],
|
||||
params = this.params = [];
|
||||
|
||||
function addParameter(id) {
|
||||
if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
names[id] = true;
|
||||
params.push(id);
|
||||
}
|
||||
|
||||
function quoteRegExp(string) {
|
||||
return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
|
||||
}
|
||||
|
||||
this.source = pattern;
|
||||
|
||||
// Split into static segments separated by path parameter placeholders.
|
||||
// The number of segments is always 1 more than the number of parameters.
|
||||
var id, regexp, segment;
|
||||
while ((m = placeholder.exec(pattern))) {
|
||||
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
|
||||
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
|
||||
segment = pattern.substring(last, m.index);
|
||||
if (segment.indexOf('?') >= 0) break; // we're into the search part
|
||||
compiled += quoteRegExp(segment) + '(' + regexp + ')';
|
||||
addParameter(id);
|
||||
segments.push(segment);
|
||||
last = placeholder.lastIndex;
|
||||
}
|
||||
segment = pattern.substring(last);
|
||||
|
||||
// Find any search parameter names and remove them from the last segment
|
||||
var i = segment.indexOf('?');
|
||||
if (i >= 0) {
|
||||
var search = this.sourceSearch = segment.substring(i);
|
||||
segment = segment.substring(0, i);
|
||||
this.sourcePath = pattern.substring(0, last+i);
|
||||
|
||||
// Allow parameters to be separated by '?' as well as '&' to make concat() easier
|
||||
forEach(search.substring(1).split(/[&?]/), addParameter);
|
||||
} else {
|
||||
this.sourcePath = pattern;
|
||||
this.sourceSearch = '';
|
||||
}
|
||||
|
||||
compiled += quoteRegExp(segment) + '$';
|
||||
segments.push(segment);
|
||||
this.regexp = new RegExp(compiled);
|
||||
this.prefix = segments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new matcher for a pattern constructed by appending the path part and adding the
|
||||
* search parameters of the specified pattern to this pattern. The current pattern is not
|
||||
* modified. This can be understood as creating a pattern for URLs that are relative to (or
|
||||
* suffixes of) the current pattern.
|
||||
*
|
||||
* ### Example
|
||||
* The following two matchers are equivalent:
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').concat('/details?date');
|
||||
* new UrlMatcher('/user/{id}/details?q&date');
|
||||
* ```
|
||||
*
|
||||
* @param {string} pattern The pattern to append.
|
||||
* @return {UrlMatcher} A matcher for the concatenated pattern.
|
||||
*/
|
||||
UrlMatcher.prototype.concat = function (pattern) {
|
||||
// Because order of search parameters is irrelevant, we can add our own search
|
||||
// parameters to the end of the new pattern. Parse the new pattern by itself
|
||||
// and then join the bits together, but it's much easier to do this on a string level.
|
||||
return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch);
|
||||
};
|
||||
|
||||
UrlMatcher.prototype.toString = function () {
|
||||
return this.source;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the specified path against this matcher, and returns an object containing the captured
|
||||
* parameter values, or null if the path does not match. The returned object contains the values
|
||||
* of any search parameters that are mentioned in the pattern, but their value may be null if
|
||||
* they are not present in `searchParams`. This means that search parameters are always treated
|
||||
* as optional.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
|
||||
* // returns { id:'bob', q:'hello', r:null }
|
||||
* ```
|
||||
*
|
||||
* @param {string} path The URL path to match, e.g. `$location.path()`.
|
||||
* @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
|
||||
* @return {Object} The captured parameter values.
|
||||
*/
|
||||
UrlMatcher.prototype.exec = function (path, searchParams) {
|
||||
var m = this.regexp.exec(path);
|
||||
if (!m) return null;
|
||||
|
||||
var params = this.params, nTotal = params.length,
|
||||
nPath = this.segments.length-1,
|
||||
values = {}, i;
|
||||
|
||||
if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
|
||||
|
||||
for (i=0; i<nPath; i++) values[params[i]] = m[i+1];
|
||||
for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the names of all path and search parameters of this pattern in an unspecified order.
|
||||
* @return {Array.<string>} An array of parameter names. Must be treated as read-only. If the
|
||||
* pattern has no parameters, an empty array is returned.
|
||||
*/
|
||||
UrlMatcher.prototype.parameters = function () {
|
||||
return this.params;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a URL that matches this pattern by substituting the specified values
|
||||
* for the path and search parameters. Null values for path parameters are
|
||||
* treated as empty strings.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
|
||||
* // returns '/user/bob?q=yes'
|
||||
* ```
|
||||
*
|
||||
* @param {Object} values the values to substitute for the parameters in this pattern.
|
||||
* @return {string} the formatted URL (path and optionally search part).
|
||||
*/
|
||||
UrlMatcher.prototype.format = function (values) {
|
||||
var segments = this.segments, params = this.params;
|
||||
if (!values) return segments.join('');
|
||||
|
||||
var nPath = segments.length-1, nTotal = params.length,
|
||||
result = segments[0], i, search, value;
|
||||
|
||||
for (i=0; i<nPath; i++) {
|
||||
value = values[params[i]];
|
||||
// TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
|
||||
if (value != null) result += encodeURIComponent(value);
|
||||
result += segments[i+1];
|
||||
}
|
||||
for (/**/; i<nTotal; i++) {
|
||||
value = values[params[i]];
|
||||
if (value != null) {
|
||||
result += (search ? '&' : '?') + params[i] + '=' + encodeURIComponent(value);
|
||||
search = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Service. Factory for {@link UrlMatcher} instances. The factory is also available to providers
|
||||
* under the name `$urlMatcherFactoryProvider`.
|
||||
* @constructor
|
||||
* @name $urlMatcherFactory
|
||||
*/
|
||||
function $UrlMatcherFactory() {
|
||||
/**
|
||||
* Creates a {@link UrlMatcher} for the specified pattern.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#compile
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {string} pattern The URL pattern.
|
||||
* @return {UrlMatcher} The UrlMatcher.
|
||||
*/
|
||||
this.compile = function (pattern) {
|
||||
return new UrlMatcher(pattern);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the specified object is a UrlMatcher, or false otherwise.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#isMatcher
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {Object} o
|
||||
* @return {boolean}
|
||||
*/
|
||||
this.isMatcher = function (o) {
|
||||
return isObject(o) && isFunction(o.exec) && isFunction(o.format) && isFunction(o.concat);
|
||||
};
|
||||
|
||||
this.$get = function () {
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
// Register as a provider so it's available to other providers
|
||||
angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
|
||||
$UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider'];
|
||||
function $UrlRouterProvider( $urlMatcherFactory) {
|
||||
var rules = [],
|
||||
otherwise = null;
|
||||
|
||||
// Returns a string that is a prefix of all strings matching the RegExp
|
||||
function regExpPrefix(re) {
|
||||
var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
|
||||
return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
|
||||
}
|
||||
|
||||
// Interpolates matched values into a String.replace()-style pattern
|
||||
function interpolate(pattern, match) {
|
||||
return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
|
||||
return match[what === '$' ? 0 : Number(what)];
|
||||
});
|
||||
}
|
||||
|
||||
this.rule =
|
||||
function (rule) {
|
||||
if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
rules.push(rule);
|
||||
return this;
|
||||
};
|
||||
|
||||
this.otherwise =
|
||||
function (rule) {
|
||||
if (isString(rule)) {
|
||||
var redirect = rule;
|
||||
rule = function () { return redirect; };
|
||||
}
|
||||
else if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
otherwise = rule;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
function handleIfMatch($injector, handler, match) {
|
||||
if (!match) return false;
|
||||
var result = $injector.invoke(handler, handler, { $match: match });
|
||||
return isDefined(result) ? result : true;
|
||||
}
|
||||
|
||||
this.when =
|
||||
function (what, handler) {
|
||||
var redirect, handlerIsString = isString(handler);
|
||||
if (isString(what)) what = $urlMatcherFactory.compile(what);
|
||||
|
||||
if (!handlerIsString && !isFunction(handler) && !isArray(handler))
|
||||
throw new Error("invalid 'handler' in when()");
|
||||
|
||||
var strategies = {
|
||||
matcher: function (what, handler) {
|
||||
if (handlerIsString) {
|
||||
redirect = $urlMatcherFactory.compile(handler);
|
||||
handler = ['$match', function ($match) { return redirect.format($match); }];
|
||||
}
|
||||
return extend(function ($injector, $location) {
|
||||
return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
|
||||
}, {
|
||||
prefix: isString(what.prefix) ? what.prefix : ''
|
||||
});
|
||||
},
|
||||
regex: function (what, handler) {
|
||||
if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
|
||||
|
||||
if (handlerIsString) {
|
||||
redirect = handler;
|
||||
handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
|
||||
}
|
||||
return extend(function ($injector, $location) {
|
||||
return handleIfMatch($injector, handler, what.exec($location.path()));
|
||||
}, {
|
||||
prefix: regExpPrefix(what)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
|
||||
|
||||
for (var n in check) {
|
||||
if (check[n]) {
|
||||
return this.rule(strategies[n](what, handler));
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("invalid 'what' in when()");
|
||||
};
|
||||
|
||||
this.$get =
|
||||
[ '$location', '$rootScope', '$injector',
|
||||
function ($location, $rootScope, $injector) {
|
||||
// TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
|
||||
function update(evt) {
|
||||
if (evt && evt.defaultPrevented) return;
|
||||
function check(rule) {
|
||||
var handled = rule($injector, $location);
|
||||
if (handled) {
|
||||
if (isString(handled)) $location.replace().url(handled);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var n=rules.length, i;
|
||||
for (i=0; i<n; i++) {
|
||||
if (check(rules[i])) return;
|
||||
}
|
||||
// always check otherwise last to allow dynamic updates to the set of rules
|
||||
if (otherwise) check(otherwise);
|
||||
}
|
||||
|
||||
$rootScope.$on('$locationChangeSuccess', update);
|
||||
|
||||
return {
|
||||
sync: function () {
|
||||
update();
|
||||
}
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
|
||||
$ViewProvider.$inject = [];
|
||||
function $ViewProvider() {
|
||||
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$rootScope', '$templateFactory'];
|
||||
function $get( $rootScope, $templateFactory) {
|
||||
return {
|
||||
// $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
|
||||
load: function load(name, options) {
|
||||
var result, defaults = {
|
||||
template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
|
||||
};
|
||||
options = extend(defaults, options);
|
||||
|
||||
if (options.view) {
|
||||
result = $templateFactory.fromConfig(options.view, options.params, options.locals);
|
||||
}
|
||||
if (result && options.notify) {
|
||||
$rootScope.$broadcast('$viewContentLoading', options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.router.state').provider('$view', $ViewProvider);
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
|
||||
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$anchorScroll'];
|
||||
function $ViewDirective( $state, $compile, $controller, $injector, $anchorScroll) {
|
||||
var $animator = $injector.has('$animator') ? $injector.get('$animator') : false;
|
||||
var viewIsUpdating = false;
|
||||
|
||||
var directive = {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
priority: 1000,
|
||||
transclude: true,
|
||||
compile: function (element, attr, transclude) {
|
||||
return function(scope, element, attr) {
|
||||
var viewScope, viewLocals,
|
||||
name = attr[directive.name] || attr.name || '',
|
||||
onloadExp = attr.onload || '',
|
||||
animate = $animator && $animator(scope, attr),
|
||||
initialView = transclude(scope);
|
||||
|
||||
// Returns a set of DOM manipulation functions based on whether animation
|
||||
// should be performed
|
||||
var renderer = function(doAnimate) {
|
||||
return ({
|
||||
"true": {
|
||||
remove: function(element) { animate.leave(element.contents(), element); },
|
||||
restore: function(compiled, element) { animate.enter(compiled, element); },
|
||||
populate: function(template, element) {
|
||||
var contents = angular.element('<div></div>').html(template).contents();
|
||||
animate.enter(contents, element);
|
||||
return contents;
|
||||
}
|
||||
},
|
||||
"false": {
|
||||
remove: function(element) { element.html(''); },
|
||||
restore: function(compiled, element) { element.append(compiled); },
|
||||
populate: function(template, element) {
|
||||
element.html(template);
|
||||
return element.contents();
|
||||
}
|
||||
}
|
||||
})[doAnimate.toString()];
|
||||
};
|
||||
|
||||
// Put back the compiled initial view
|
||||
element.append(initialView);
|
||||
|
||||
// Find the details of the parent view directive (if any) and use it
|
||||
// to derive our own qualified view name, then hang our own details
|
||||
// off the DOM so child directives can find it.
|
||||
var parent = element.parent().inheritedData('$uiView');
|
||||
if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : '');
|
||||
var view = { name: name, state: null };
|
||||
element.data('$uiView', view);
|
||||
|
||||
var eventHook = function() {
|
||||
if (viewIsUpdating) return;
|
||||
viewIsUpdating = true;
|
||||
|
||||
try { updateView(true); } catch (e) {
|
||||
viewIsUpdating = false;
|
||||
throw e;
|
||||
}
|
||||
viewIsUpdating = false;
|
||||
};
|
||||
|
||||
scope.$on('$stateChangeSuccess', eventHook);
|
||||
scope.$on('$viewContentLoading', eventHook);
|
||||
updateView(false);
|
||||
|
||||
function updateView(doAnimate) {
|
||||
var locals = $state.$current && $state.$current.locals[name];
|
||||
if (locals === viewLocals) return; // nothing to do
|
||||
var render = renderer(animate && doAnimate);
|
||||
|
||||
// Remove existing content
|
||||
render.remove(element);
|
||||
|
||||
// Destroy previous view scope
|
||||
if (viewScope) {
|
||||
viewScope.$destroy();
|
||||
viewScope = null;
|
||||
}
|
||||
|
||||
if (!locals) {
|
||||
viewLocals = null;
|
||||
view.state = null;
|
||||
|
||||
// Restore the initial view
|
||||
return render.restore(initialView, element);
|
||||
}
|
||||
|
||||
viewLocals = locals;
|
||||
view.state = locals.$$state;
|
||||
|
||||
var link = $compile(render.populate(locals.$template, element));
|
||||
viewScope = scope.$new();
|
||||
|
||||
if (locals.$$controller) {
|
||||
locals.$scope = viewScope;
|
||||
var controller = $controller(locals.$$controller, locals);
|
||||
element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
link(viewScope);
|
||||
viewScope.$emit('$viewContentLoaded');
|
||||
if (onloadExp) viewScope.$eval(onloadExp);
|
||||
|
||||
// TODO: This seems strange, shouldn't $anchorScroll listen for $viewContentLoaded if necessary?
|
||||
// $anchorScroll might listen on event...
|
||||
$anchorScroll();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
angular.module('ui.router.state').directive('uiView', $ViewDirective);
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
beforeEach(function() {
|
||||
|
||||
function cssMatcher(presentClasses, absentClasses) {
|
||||
return function() {
|
||||
var element = angular.element(this.actual);
|
||||
var present = true;
|
||||
var absent = false;
|
||||
|
||||
angular.forEach(presentClasses.split(' '), function(className){
|
||||
present = present && element.hasClass(className);
|
||||
});
|
||||
|
||||
angular.forEach(absentClasses.split(' '), function(className){
|
||||
absent = absent || element.hasClass(className);
|
||||
});
|
||||
|
||||
this.message = function() {
|
||||
return "Expected to have " + presentClasses +
|
||||
(absentClasses ? (" and not have " + absentClasses + "" ) : "") +
|
||||
" but had " + element[0].className + ".";
|
||||
};
|
||||
return present && !absent;
|
||||
};
|
||||
}
|
||||
|
||||
function indexOf(array, obj) {
|
||||
for ( var i = 0; i < array.length; i++) {
|
||||
if (obj === array[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
this.addMatchers({
|
||||
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
|
||||
toBeValid: cssMatcher('ng-valid', 'ng-invalid'),
|
||||
toBeDirty: cssMatcher('ng-dirty', 'ng-pristine'),
|
||||
toBePristine: cssMatcher('ng-pristine', 'ng-dirty'),
|
||||
|
||||
toEqual: function(expected) {
|
||||
if (this.actual && this.actual.$$log) {
|
||||
this.actual = (typeof expected === 'string')
|
||||
? this.actual.toString()
|
||||
: this.actual.toArray();
|
||||
}
|
||||
return jasmine.Matchers.prototype.toEqual.call(this, expected);
|
||||
},
|
||||
|
||||
toEqualData: function(expected) {
|
||||
return angular.equals(this.actual, expected);
|
||||
},
|
||||
|
||||
toEqualError: function(message) {
|
||||
this.message = function() {
|
||||
var expected;
|
||||
if (this.actual.message && this.actual.name == 'Error') {
|
||||
expected = toJson(this.actual.message);
|
||||
} else {
|
||||
expected = toJson(this.actual);
|
||||
}
|
||||
return "Expected " + expected + " to be an Error with message " + toJson(message);
|
||||
};
|
||||
return this.actual.name == 'Error' && this.actual.message == message;
|
||||
},
|
||||
|
||||
toMatchError: function(messageRegexp) {
|
||||
this.message = function() {
|
||||
var expected;
|
||||
if (this.actual.message && this.actual.name == 'Error') {
|
||||
expected = angular.toJson(this.actual.message);
|
||||
} else {
|
||||
expected = angular.toJson(this.actual);
|
||||
}
|
||||
return "Expected " + expected + " to match an Error with message " + angular.toJson(messageRegexp);
|
||||
};
|
||||
return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
|
||||
},
|
||||
|
||||
toHaveBeenCalledOnce: function() {
|
||||
if (arguments.length > 0) {
|
||||
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
|
||||
}
|
||||
|
||||
if (!jasmine.isSpy(this.actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
var msg = 'Expected spy ' + this.actual.identity + ' to have been called once, but was ',
|
||||
count = this.actual.callCount;
|
||||
return [
|
||||
count === 0 ? msg + 'never called.' :
|
||||
msg + 'called ' + count + ' times.',
|
||||
msg.replace('to have', 'not to have') + 'called once.'
|
||||
];
|
||||
};
|
||||
|
||||
return this.actual.callCount == 1;
|
||||
},
|
||||
|
||||
|
||||
toHaveBeenCalledOnceWith: function() {
|
||||
var expectedArgs = jasmine.util.argsToArray(arguments);
|
||||
|
||||
if (!jasmine.isSpy(this.actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
if (this.actual.callCount != 1) {
|
||||
if (this.actual.callCount == 0) {
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was never called.',
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was.'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was called ' + this.actual.callCount + ' times.',
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but it was.'
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall),
|
||||
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
|
||||
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall)
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
return this.actual.callCount === 1 && this.env.contains_(this.actual.argsForCall, expectedArgs);
|
||||
},
|
||||
|
||||
|
||||
toBeOneOf: function() {
|
||||
return indexOf(arguments, this.actual) !== -1;
|
||||
},
|
||||
|
||||
toHaveClass: function(clazz) {
|
||||
this.message = function() {
|
||||
return "Expected '" + angular.mock.dump(this.actual) + "' to have class '" + clazz + "'.";
|
||||
};
|
||||
return this.actual.hasClass ?
|
||||
this.actual.hasClass(clazz) :
|
||||
angular.element(this.actual).hasClass(clazz);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
// 'use strict';
|
||||
|
||||
// describe('$routeParams', function() {
|
||||
// it('should publish the params into a service', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo', {});
|
||||
// $routeProvider.when('/bar/:barId', {});
|
||||
// });
|
||||
|
||||
// inject(function($rootScope, $route, $location, $routeParams) {
|
||||
// $location.path('/foo').search('a=b');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({a:'b'});
|
||||
|
||||
// $location.path('/bar/123').search('x=abc');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({barId:'123', x:'abc'});
|
||||
// });
|
||||
// });
|
||||
|
||||
// it('should correctly extract the params when a param name is part of the route', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/bar/:foo/:bar', {});
|
||||
// });
|
||||
|
||||
// inject(function($rootScope, $route, $location, $routeParams) {
|
||||
// $location.path('/bar/foovalue/barvalue');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({bar:'barvalue', foo:'foovalue'});
|
||||
// });
|
||||
// });
|
||||
|
||||
// it('should support route params not preceded by slashes', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/bar:barId/foo:fooId/', {});
|
||||
// });
|
||||
|
||||
// inject(function($rootScope, $route, $location, $routeParams) {
|
||||
// $location.path('/barbarvalue/foofoovalue/');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({barId: 'barvalue', fooId: 'foovalue'});
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
+919
@@ -0,0 +1,919 @@
|
||||
'use strict';
|
||||
|
||||
describe('$route', function() {
|
||||
var $httpBackend;
|
||||
|
||||
beforeEach(module(function() {
|
||||
return function(_$httpBackend_) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.when('GET', 'Chapter.html').respond('chapter');
|
||||
$httpBackend.when('GET', 'test.html').respond('test');
|
||||
$httpBackend.when('GET', 'foo.html').respond('foo');
|
||||
$httpBackend.when('GET', 'baz.html').respond('baz');
|
||||
$httpBackend.when('GET', 'bar.html').respond('bar');
|
||||
$httpBackend.when('GET', '404.html').respond('not found');
|
||||
};
|
||||
}));
|
||||
|
||||
it('should route and fire change event', function() {
|
||||
var log = '',
|
||||
lastRoute,
|
||||
nextRoute;
|
||||
|
||||
module(function($routeProvider) {
|
||||
$routeProvider.when('/Book/:book/Chapter/:chapter',
|
||||
{controller: noop, templateUrl: 'Chapter.html'});
|
||||
$routeProvider.when('/Blank', {});
|
||||
});
|
||||
inject(function($route, $location, $rootScope) {
|
||||
$rootScope.$on('$routeChangeStart', function(event, next, current) {
|
||||
log += 'before();';
|
||||
expect(current).toBe($route.current);
|
||||
lastRoute = current;
|
||||
nextRoute = next;
|
||||
});
|
||||
$rootScope.$on('$routeChangeSuccess', function(event, current, last) {
|
||||
log += 'after();';
|
||||
expect(current).toBe($route.current);
|
||||
expect(lastRoute).toBe(last);
|
||||
expect(nextRoute).toBe(current);
|
||||
});
|
||||
|
||||
$location.path('/Book/Moby/Chapter/Intro').search('p=123');
|
||||
$rootScope.$digest();
|
||||
$httpBackend.flush();
|
||||
expect(log).toEqual('before();after();');
|
||||
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', p:'123'});
|
||||
|
||||
log = '';
|
||||
$location.path('/Blank').search('ignore');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual('before();after();');
|
||||
expect($route.current.params).toEqual({ignore:true});
|
||||
|
||||
log = '';
|
||||
$location.path('/NONE');
|
||||
$rootScope.$digest();
|
||||
expect(log).toEqual('before();after();');
|
||||
expect($route.current).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
// it('should route and fire change event when catch-all params are used', function() {
|
||||
// var log = '',
|
||||
// lastRoute,
|
||||
// nextRoute;
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
|
||||
// {controller: noop, templateUrl: 'Chapter.html'});
|
||||
// $routeProvider.when('/Book2/:book/*highlight/Chapter/:chapter',
|
||||
// {controller: noop, templateUrl: 'Chapter.html'});
|
||||
// $routeProvider.when('/Blank', {});
|
||||
// });
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $rootScope.$on('$routeChangeStart', function(event, next, current) {
|
||||
// log += 'before();';
|
||||
// expect(current).toBe($route.current);
|
||||
// lastRoute = current;
|
||||
// nextRoute = next;
|
||||
// });
|
||||
// $rootScope.$on('$routeChangeSuccess', function(event, current, last) {
|
||||
// log += 'after();';
|
||||
// expect(current).toBe($route.current);
|
||||
// expect(lastRoute).toBe(last);
|
||||
// expect(nextRoute).toBe(current);
|
||||
// });
|
||||
|
||||
// $location.path('/Book1/Moby/Chapter/Intro/one/edit').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/Blank').search('ignore');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({ignore:true});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/Book1/Moby/Chapter/Intro/one/two/edit').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/NONE');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current).toEqual(null);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should route and fire change event correctly whenever the case insensitive flag is utilized', function() {
|
||||
// var log = '',
|
||||
// lastRoute,
|
||||
// nextRoute;
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
|
||||
// {controller: noop, templateUrl: 'Chapter.html', caseInsensitiveMatch: true});
|
||||
// $routeProvider.when('/Book2/:book/*highlight/Chapter/:chapter',
|
||||
// {controller: noop, templateUrl: 'Chapter.html'});
|
||||
// $routeProvider.when('/Blank', {});
|
||||
// });
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $rootScope.$on('$routeChangeStart', function(event, next, current) {
|
||||
// log += 'before();';
|
||||
// expect(current).toBe($route.current);
|
||||
// lastRoute = current;
|
||||
// nextRoute = next;
|
||||
// });
|
||||
// $rootScope.$on('$routeChangeSuccess', function(event, current, last) {
|
||||
// log += 'after();';
|
||||
// expect(current).toBe($route.current);
|
||||
// expect(lastRoute).toBe(last);
|
||||
// expect(nextRoute).toBe(current);
|
||||
// });
|
||||
|
||||
// $location.path('/Book1/Moby/Chapter/Intro/one/edit').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/BOOK1/Moby/CHAPTER/Intro/one/EDIT').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/Blank').search('ignore');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({ignore:true});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/BLANK');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current).toEqual(null);
|
||||
|
||||
// log = '';
|
||||
// $location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
|
||||
|
||||
// log = '';
|
||||
// $location.path('/BOOK2/Moby/one/two/CHAPTER/Intro').search('p=123');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();after();');
|
||||
// expect($route.current).toEqual(null);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should not change route when location is canceled', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/somePath', {template: 'some path'});
|
||||
// });
|
||||
// inject(function($route, $location, $rootScope, $log) {
|
||||
// $rootScope.$on('$locationChangeStart', function(event) {
|
||||
// $log.info('$locationChangeStart');
|
||||
// event.preventDefault();
|
||||
// });
|
||||
|
||||
// $rootScope.$on('$beforeRouteChange', function(event) {
|
||||
// throw new Error('Should not get here');
|
||||
// });
|
||||
|
||||
// $location.path('/somePath');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($log.info.logs.shift()).toEqual(['$locationChangeStart']);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// describe('should match a route that contains special chars in the path', function() {
|
||||
// beforeEach(module(function($routeProvider) {
|
||||
// $routeProvider.when('/$test.23/foo*(bar)/:baz', {templateUrl: 'test.html'});
|
||||
// }));
|
||||
|
||||
// it('matches the full path', inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/test');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current).toBeUndefined();
|
||||
// }));
|
||||
|
||||
// it('matches literal .', inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/$testX23/foo*(bar)/222');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current).toBeUndefined();
|
||||
// }));
|
||||
|
||||
// it('matches literal *', inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/$test.23/foooo(bar)/222');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current).toBeUndefined();
|
||||
// }));
|
||||
|
||||
// it('treats backslashes normally', inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/$test.23/foo*\\(bar)/222');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current).toBeUndefined();
|
||||
// }));
|
||||
|
||||
// it('matches a URL with special chars', inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/$test.23/foo*(bar)/222');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current).toBeDefined();
|
||||
// }));
|
||||
// });
|
||||
|
||||
|
||||
// it('should change route even when only search param changes', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/test', {templateUrl: 'test.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// var callback = jasmine.createSpy('onRouteChange');
|
||||
|
||||
// $rootScope.$on('$routeChangeStart', callback);
|
||||
// $location.path('/test');
|
||||
// $rootScope.$digest();
|
||||
// callback.reset();
|
||||
|
||||
// $location.search({any: true});
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect(callback).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should allow routes to be defined with just templates without controllers', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo', {templateUrl: 'foo.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// var onChangeSpy = jasmine.createSpy('onChange');
|
||||
|
||||
// $rootScope.$on('$routeChangeStart', onChangeSpy);
|
||||
// expect($route.current).toBeUndefined();
|
||||
// expect(onChangeSpy).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($route.current.templateUrl).toEqual('foo.html');
|
||||
// expect($route.current.controller).toBeUndefined();
|
||||
// expect(onChangeSpy).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should handle unknown routes with "otherwise" route definition', function() {
|
||||
// function NotFoundCtrl() {}
|
||||
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/foo', {templateUrl: 'foo.html'});
|
||||
// $routeProvider.otherwise({templateUrl: '404.html', controller: NotFoundCtrl});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// var onChangeSpy = jasmine.createSpy('onChange');
|
||||
|
||||
// $rootScope.$on('$routeChangeStart', onChangeSpy);
|
||||
// expect($route.current).toBeUndefined();
|
||||
// expect(onChangeSpy).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/unknownRoute');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($route.current.templateUrl).toBe('404.html');
|
||||
// expect($route.current.controller).toBe(NotFoundCtrl);
|
||||
// expect(onChangeSpy).toHaveBeenCalled();
|
||||
|
||||
// onChangeSpy.reset();
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($route.current.templateUrl).toEqual('foo.html');
|
||||
// expect($route.current.controller).toBeUndefined();
|
||||
// expect(onChangeSpy).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should chain whens and otherwise', function() {
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/foo', {templateUrl: 'foo.html'}).
|
||||
// otherwise({templateUrl: 'bar.html'}).
|
||||
// when('/baz', {templateUrl: 'baz.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current.templateUrl).toBe('bar.html');
|
||||
|
||||
// $location.url('/baz');
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current.templateUrl).toBe('baz.html');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// describe('events', function() {
|
||||
// it('should not fire $after/beforeRouteChange during bootstrap (if no route)', function() {
|
||||
// var routeChangeSpy = jasmine.createSpy('route change');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/one', {}); // no otherwise defined
|
||||
// });
|
||||
|
||||
// inject(function($rootScope, $route, $location) {
|
||||
// $rootScope.$on('$routeChangeStart', routeChangeSpy);
|
||||
// $rootScope.$on('$routeChangeSuccess', routeChangeSpy);
|
||||
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChangeSpy).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/no-route-here');
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChangeSpy).not.toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
// it('should fire $routeChangeStart and resolve promises', function() {
|
||||
// var deferA,
|
||||
// deferB;
|
||||
|
||||
// module(function($provide, $routeProvider) {
|
||||
// $provide.factory('b', function($q) {
|
||||
// deferB = $q.defer();
|
||||
// return deferB.promise;
|
||||
// });
|
||||
// $routeProvider.when('/path', { templateUrl: 'foo.html', resolve: {
|
||||
// a: ['$q', function($q) {
|
||||
// deferA = $q.defer();
|
||||
// return deferA.promise;
|
||||
// }],
|
||||
// b: 'b'
|
||||
// } });
|
||||
// });
|
||||
|
||||
// inject(function($location, $route, $rootScope, $httpBackend) {
|
||||
// var log = '';
|
||||
|
||||
// $httpBackend.expectGET('foo.html').respond('FOO');
|
||||
|
||||
// $location.path('/path');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('');
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toEqual('');
|
||||
// deferA.resolve();
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('');
|
||||
// deferB.resolve();
|
||||
// $rootScope.$digest();
|
||||
// expect($route.current.locals.$template).toEqual('FOO');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should fire $routeChangeError event on resolution error', function() {
|
||||
// var deferA;
|
||||
|
||||
// module(function($provide, $routeProvider) {
|
||||
// $routeProvider.when('/path', { template: 'foo', resolve: {
|
||||
// a: function($q) {
|
||||
// deferA = $q.defer();
|
||||
// return deferA.promise;
|
||||
// }
|
||||
// } });
|
||||
// });
|
||||
|
||||
// inject(function($location, $route, $rootScope) {
|
||||
// var log = '';
|
||||
|
||||
// $rootScope.$on('$routeChangeStart', function() { log += 'before();'; });
|
||||
// $rootScope.$on('$routeChangeError', function(e, n, l, reason) { log += 'failed(' + reason + ');'; });
|
||||
|
||||
// $location.path('/path');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();');
|
||||
|
||||
// deferA.reject('MyError');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toEqual('before();failed(MyError);');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should fetch templates', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.
|
||||
// when('/r1', { templateUrl: 'r1.html' }).
|
||||
// when('/r2', { templateUrl: 'r2.html' });
|
||||
// });
|
||||
|
||||
// inject(function($route, $httpBackend, $location, $rootScope) {
|
||||
// var log = '';
|
||||
// $rootScope.$on('$routeChangeStart', function(e, next) { log += '$before(' + next.templateUrl + ');'});
|
||||
// $rootScope.$on('$routeChangeSuccess', function(e, next) { log += '$after(' + next.templateUrl + ');'});
|
||||
|
||||
// $httpBackend.expectGET('r1.html').respond('R1');
|
||||
// $httpBackend.expectGET('r2.html').respond('R2');
|
||||
|
||||
// $location.path('/r1');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);');
|
||||
|
||||
// $location.path('/r2');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);');
|
||||
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);$after(r2.html);');
|
||||
// expect(log).not.toContain('$after(r1.html);');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should not update $routeParams until $routeChangeSuccess', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.
|
||||
// when('/r1/:id', { templateUrl: 'r1.html' }).
|
||||
// when('/r2/:id', { templateUrl: 'r2.html' });
|
||||
// });
|
||||
|
||||
// inject(function($route, $httpBackend, $location, $rootScope, $routeParams) {
|
||||
// var log = '';
|
||||
// $rootScope.$on('$routeChangeStart', function(e, next) { log += '$before' + toJson($routeParams) + ';'});
|
||||
// $rootScope.$on('$routeChangeSuccess', function(e, next) { log += '$after' + toJson($routeParams) + ';'});
|
||||
|
||||
// $httpBackend.whenGET('r1.html').respond('R1');
|
||||
// $httpBackend.whenGET('r2.html').respond('R2');
|
||||
|
||||
// $location.path('/r1/1');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before{};');
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toBe('$before{};$after{"id":"1"};');
|
||||
|
||||
// log = '';
|
||||
|
||||
// $location.path('/r2/2');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before{"id":"1"};');
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toBe('$before{"id":"1"};$after{"id":"2"};');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should drop in progress route change when new route change occurs', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.
|
||||
// when('/r1', { templateUrl: 'r1.html' }).
|
||||
// when('/r2', { templateUrl: 'r2.html' });
|
||||
// });
|
||||
|
||||
// inject(function($route, $httpBackend, $location, $rootScope) {
|
||||
// var log = '';
|
||||
// $rootScope.$on('$routeChangeStart', function(e, next) { log += '$before(' + next.templateUrl + ');'});
|
||||
// $rootScope.$on('$routeChangeSuccess', function(e, next) { log += '$after(' + next.templateUrl + ');'});
|
||||
|
||||
// $httpBackend.expectGET('r1.html').respond('R1');
|
||||
// $httpBackend.expectGET('r2.html').respond('R2');
|
||||
|
||||
// $location.path('/r1');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);');
|
||||
|
||||
// $location.path('/r2');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);');
|
||||
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);$after(r2.html);');
|
||||
// expect(log).not.toContain('$after(r1.html);');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should drop in progress route change when new route change occurs and old fails', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.
|
||||
// when('/r1', { templateUrl: 'r1.html' }).
|
||||
// when('/r2', { templateUrl: 'r2.html' });
|
||||
// });
|
||||
|
||||
// inject(function($route, $httpBackend, $location, $rootScope) {
|
||||
// var log = '';
|
||||
// $rootScope.$on('$routeChangeError', function(e, next, last, error) {
|
||||
// log += '$failed(' + next.templateUrl + ', ' + error.status + ');';
|
||||
// });
|
||||
// $rootScope.$on('$routeChangeStart', function(e, next) { log += '$before(' + next.templateUrl + ');'});
|
||||
// $rootScope.$on('$routeChangeSuccess', function(e, next) { log += '$after(' + next.templateUrl + ');'});
|
||||
|
||||
// $httpBackend.expectGET('r1.html').respond(404, 'R1');
|
||||
// $httpBackend.expectGET('r2.html').respond('R2');
|
||||
|
||||
// $location.path('/r1');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);');
|
||||
|
||||
// $location.path('/r2');
|
||||
// $rootScope.$digest();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);');
|
||||
|
||||
// $httpBackend.flush();
|
||||
// expect(log).toBe('$before(r1.html);$before(r2.html);$after(r2.html);');
|
||||
// expect(log).not.toContain('$after(r1.html);');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should catch local factory errors', function() {
|
||||
// var myError = new Error('MyError');
|
||||
// module(function($routeProvider, $exceptionHandlerProvider) {
|
||||
// $exceptionHandlerProvider.mode('log');
|
||||
// $routeProvider.when('/locals', {
|
||||
// resolve: {
|
||||
// a: function($q) {
|
||||
// throw myError;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// inject(function($location, $route, $rootScope, $exceptionHandler) {
|
||||
// $location.path('/locals');
|
||||
// $rootScope.$digest();
|
||||
// expect($exceptionHandler.errors).toEqual([myError]);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should match route with and without trailing slash', function() {
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/foo', {templateUrl: 'foo.html'});
|
||||
// $routeProvider.when('/bar/', {templateUrl: 'bar.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/foo');
|
||||
// expect($route.current.templateUrl).toBe('foo.html');
|
||||
|
||||
// $location.path('/foo/');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/foo');
|
||||
// expect($route.current.templateUrl).toBe('foo.html');
|
||||
|
||||
// $location.path('/bar');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/bar/');
|
||||
// expect($route.current.templateUrl).toBe('bar.html');
|
||||
|
||||
// $location.path('/bar/');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/bar/');
|
||||
// expect($route.current.templateUrl).toBe('bar.html');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// describe('redirection', function() {
|
||||
// it('should support redirection via redirectTo property by updating $location', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/', {redirectTo: '/foo'});
|
||||
// $routeProvider.when('/foo', {templateUrl: 'foo.html'});
|
||||
// $routeProvider.when('/bar', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/baz', {redirectTo: '/bar'});
|
||||
// $routeProvider.otherwise({templateUrl: '404.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// var onChangeSpy = jasmine.createSpy('onChange');
|
||||
|
||||
// $rootScope.$on('$routeChangeStart', onChangeSpy);
|
||||
// expect($route.current).toBeUndefined();
|
||||
// expect(onChangeSpy).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/foo');
|
||||
// expect($route.current.templateUrl).toBe('foo.html');
|
||||
// expect(onChangeSpy.callCount).toBe(2);
|
||||
|
||||
// onChangeSpy.reset();
|
||||
// $location.path('/baz');
|
||||
// $rootScope.$digest();
|
||||
// expect($location.path()).toBe('/bar');
|
||||
// expect($route.current.templateUrl).toBe('bar.html');
|
||||
// expect(onChangeSpy.callCount).toBe(2);
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should interpolate route vars in the redirected path from original path', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo/:id/foo/:subid/:extraId', {redirectTo: '/bar/:id/:subid/23'});
|
||||
// $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo/id1/foo/subid3/gah');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($location.path()).toEqual('/bar/id1/subid3/23');
|
||||
// expect($location.search()).toEqual({extraId: 'gah'});
|
||||
// expect($route.current.templateUrl).toEqual('bar.html');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should interpolate route vars in the redirected path from original search', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id/:subid/99'});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo/id3/eId').search('subid=sid1&appended=true');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($location.path()).toEqual('/bar/id3/sid1/99');
|
||||
// expect($location.search()).toEqual({appended: 'true', extra: 'eId'});
|
||||
// expect($route.current.templateUrl).toEqual('bar.html');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should allow custom redirectTo function to be used', function() {
|
||||
// function customRedirectFn(routePathParams, path, search) {
|
||||
// expect(routePathParams).toEqual({id: 'id3'});
|
||||
// expect(path).toEqual('/foo/id3');
|
||||
// expect(search).toEqual({ subid: 'sid1', appended: 'true' });
|
||||
// return '/custom';
|
||||
// }
|
||||
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/foo/:id', {redirectTo: customRedirectFn});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo/id3').search('subid=sid1&appended=true');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($location.path()).toEqual('/custom');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should replace the url when redirecting', function() {
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/bar/:id', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id'});
|
||||
// });
|
||||
// inject(function($browser, $route, $location, $rootScope) {
|
||||
// var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough();
|
||||
|
||||
// $location.path('/foo/id3/eId');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect($location.path()).toEqual('/bar/id3');
|
||||
// expect($browserUrl.mostRecentCall.args)
|
||||
// .toEqual(['http://server/#/bar/id3?extra=eId', true]);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// describe('reloadOnSearch', function() {
|
||||
// it('should reload a route when reloadOnSearch is enabled and .search() changes', function() {
|
||||
// var reloaded = jasmine.createSpy('route reload');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo', {controller: noop});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope, $routeParams) {
|
||||
// $rootScope.$on('$routeChangeStart', reloaded);
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
// expect(reloaded).toHaveBeenCalled();
|
||||
// expect($routeParams).toEqual({});
|
||||
// reloaded.reset();
|
||||
|
||||
// // trigger reload
|
||||
// $location.search({foo: 'bar'});
|
||||
// $rootScope.$digest();
|
||||
// expect(reloaded).toHaveBeenCalled();
|
||||
// expect($routeParams).toEqual({foo:'bar'});
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should not reload a route when reloadOnSearch is disabled and only .search() changes', function() {
|
||||
// var routeChange = jasmine.createSpy('route change'),
|
||||
// routeUpdate = jasmine.createSpy('route update');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo', {controller: noop, reloadOnSearch: false});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $rootScope.$on('$routeChangeStart', routeChange);
|
||||
// $rootScope.$on('$routeChangeSuccess', routeChange);
|
||||
// $rootScope.$on('$routeUpdate', routeUpdate);
|
||||
|
||||
// expect(routeChange).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChange).toHaveBeenCalled();
|
||||
// expect(routeChange.callCount).toBe(2);
|
||||
// expect(routeUpdate).not.toHaveBeenCalled();
|
||||
// routeChange.reset();
|
||||
|
||||
// // don't trigger reload
|
||||
// $location.search({foo: 'bar'});
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChange).not.toHaveBeenCalled();
|
||||
// expect(routeUpdate).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should reload reloadOnSearch route when url differs only in route path param', function() {
|
||||
// var routeChange = jasmine.createSpy('route change');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo/:fooId', {controller: noop, reloadOnSearch: false});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $rootScope.$on('$routeChangeStart', routeChange);
|
||||
// $rootScope.$on('$routeChangeSuccess', routeChange);
|
||||
|
||||
// expect(routeChange).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/foo/aaa');
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChange).toHaveBeenCalled();
|
||||
// expect(routeChange.callCount).toBe(2);
|
||||
// routeChange.reset();
|
||||
|
||||
// $location.path('/foo/bbb');
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChange).toHaveBeenCalled();
|
||||
// expect(routeChange.callCount).toBe(2);
|
||||
// routeChange.reset();
|
||||
|
||||
// $location.search({foo: 'bar'});
|
||||
// $rootScope.$digest();
|
||||
// expect(routeChange).not.toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should update params when reloadOnSearch is disabled and .search() changes', function() {
|
||||
// var routeParamsWatcher = jasmine.createSpy('routeParamsWatcher');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/foo', {controller: noop});
|
||||
// $routeProvider.when('/bar/:barId', {controller: noop, reloadOnSearch: false});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope, $routeParams) {
|
||||
// $rootScope.$watch(function() {
|
||||
// return $routeParams;
|
||||
// }, function(value) {
|
||||
// routeParamsWatcher(value);
|
||||
// }, true);
|
||||
|
||||
// expect(routeParamsWatcher).not.toHaveBeenCalled();
|
||||
|
||||
// $location.path('/foo');
|
||||
// $rootScope.$digest();
|
||||
// expect(routeParamsWatcher).toHaveBeenCalledWith({});
|
||||
// routeParamsWatcher.reset();
|
||||
|
||||
// // trigger reload
|
||||
// $location.search({foo: 'bar'});
|
||||
// $rootScope.$digest();
|
||||
// expect(routeParamsWatcher).toHaveBeenCalledWith({foo: 'bar'});
|
||||
// routeParamsWatcher.reset();
|
||||
|
||||
// $location.path('/bar/123').search({});
|
||||
// $rootScope.$digest();
|
||||
// expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123'});
|
||||
// routeParamsWatcher.reset();
|
||||
|
||||
// // don't trigger reload
|
||||
// $location.search({foo: 'bar'});
|
||||
// $rootScope.$digest();
|
||||
// expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should allow using a function as a template', function() {
|
||||
// var customTemplateWatcher = jasmine.createSpy('customTemplateWatcher');
|
||||
|
||||
// function customTemplateFn(routePathParams) {
|
||||
// customTemplateWatcher(routePathParams);
|
||||
// expect(routePathParams).toEqual({id: 'id3'});
|
||||
// return '<h1>' + routePathParams.id + '</h1>';
|
||||
// }
|
||||
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/foo/:id', {template: customTemplateFn});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo/id3');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect(customTemplateWatcher).toHaveBeenCalledWith({id: 'id3'});
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// it('should allow using a function as a templateUrl', function() {
|
||||
// var customTemplateUrlWatcher = jasmine.createSpy('customTemplateUrlWatcher');
|
||||
|
||||
// function customTemplateUrlFn(routePathParams) {
|
||||
// customTemplateUrlWatcher(routePathParams);
|
||||
// expect(routePathParams).toEqual({id: 'id3'});
|
||||
// return 'foo.html';
|
||||
// }
|
||||
|
||||
// module(function($routeProvider){
|
||||
// $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
|
||||
// $routeProvider.when('/foo/:id', {templateUrl: customTemplateUrlFn});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope) {
|
||||
// $location.path('/foo/id3');
|
||||
// $rootScope.$digest();
|
||||
|
||||
// expect(customTemplateUrlWatcher).toHaveBeenCalledWith({id: 'id3'});
|
||||
// expect($route.current.loadedTemplateUrl).toEqual('foo.html');
|
||||
// });
|
||||
// });
|
||||
|
||||
|
||||
// describe('reload', function() {
|
||||
|
||||
// it('should reload even if reloadOnSearch is false', function() {
|
||||
// var routeChangeSpy = jasmine.createSpy('route change');
|
||||
|
||||
// module(function($routeProvider) {
|
||||
// $routeProvider.when('/bar/:barId', {controller: noop, reloadOnSearch: false});
|
||||
// });
|
||||
|
||||
// inject(function($route, $location, $rootScope, $routeParams) {
|
||||
// $rootScope.$on('$routeChangeSuccess', routeChangeSpy);
|
||||
|
||||
// $location.path('/bar/123');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({barId:'123'});
|
||||
// expect(routeChangeSpy).toHaveBeenCalledOnce();
|
||||
// routeChangeSpy.reset();
|
||||
|
||||
// $location.path('/bar/123').search('a=b');
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({barId:'123', a:'b'});
|
||||
// expect(routeChangeSpy).not.toHaveBeenCalled();
|
||||
|
||||
// $route.reload();
|
||||
// $rootScope.$digest();
|
||||
// expect($routeParams).toEqual({barId:'123', a:'b'});
|
||||
// expect(routeChangeSpy).toHaveBeenCalledOnce();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Karma module file used for debugging UI Router within the test suite.
|
||||
* To start, open a terminal from within the UI Router project directory
|
||||
* and run `karma start test/debug.js`.
|
||||
*/
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath: '.',
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'../lib/angular-1.1.5.js',
|
||||
'./lib/angular-mocks-1.1.5.js',
|
||||
'./testUtils.js',
|
||||
|
||||
'../src/common.js',
|
||||
'../src/resolve.js',
|
||||
'../src/templateFactory.js',
|
||||
'../src/urlMatcherFactory.js',
|
||||
'../src/urlRouter.js',
|
||||
'../src/view.js',
|
||||
'../src/state.js',
|
||||
'../src/viewDirective.js',
|
||||
'../src/stateDirectives.js',
|
||||
'../src/compat.js',
|
||||
|
||||
'../test/*Spec.js',
|
||||
// 'test/compat/matchers.js',
|
||||
// 'test/compat/*Spec.js',
|
||||
],
|
||||
|
||||
frameworks: ["jasmine"],
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
// use dots reporter, as travis terminal does not support escaping sequences
|
||||
// possible values: 'dots' || 'progress'
|
||||
reporters: ['dots'],
|
||||
|
||||
// these are default values, just to show available options
|
||||
|
||||
// web server port
|
||||
port: 8080,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
|
||||
//logLevel: karma.LOG_DEBUG,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// polling interval in ms (ignored on OS that support inotify)
|
||||
autoWatchInterval: 0,
|
||||
|
||||
// Start these browsers, currently available:
|
||||
// - Chrome
|
||||
// - ChromeCanary
|
||||
// - Firefox
|
||||
// - Opera
|
||||
// - Safari
|
||||
// - PhantomJS
|
||||
browsers: [ 'Chrome' ]
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
+312
@@ -0,0 +1,312 @@
|
||||
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;
|
||||
}));
|
||||
|
||||
describe(".resolve()", function () {
|
||||
it("calls injectable functions and returns a promise", function () {
|
||||
var fun = jasmine.createSpy('fun').andReturn(42);
|
||||
var r = $r.resolve({ fun: [ '$resolve', fun ] });
|
||||
expect(r).not.toBeResolved();
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual({ fun: 42 });
|
||||
expect(fun).toHaveBeenCalled();
|
||||
expect(fun.callCount).toBe(1);
|
||||
expect(fun.mostRecentCall.args.length).toBe(1);
|
||||
expect(fun.mostRecentCall.args[0]).toBe($r);
|
||||
});
|
||||
|
||||
it("resolves promises returned from the functions", inject(function ($q) {
|
||||
var d = $q.defer();
|
||||
var fun = jasmine.createSpy('fun').andReturn(d.promise);
|
||||
var r = $r.resolve({ fun: [ '$resolve', fun ] });
|
||||
tick();
|
||||
expect(r).not.toBeResolved();
|
||||
d.resolve('async');
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual({ fun: 'async' });
|
||||
}));
|
||||
|
||||
it("resolves dependencies between functions", function () {
|
||||
var a = jasmine.createSpy('a');
|
||||
var b = jasmine.createSpy('b').andReturn('bb');
|
||||
var r = $r.resolve({ a: [ 'b', a ], b: [ b ] });
|
||||
tick();
|
||||
expect(a).toHaveBeenCalled();
|
||||
expect(a.mostRecentCall.args).toEqual([ 'bb' ]);
|
||||
expect(b).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resolves dependencies between functions that return promises", inject(function ($q) {
|
||||
var ad = $q.defer(), a = jasmine.createSpy('a').andReturn(ad.promise);
|
||||
var bd = $q.defer(), b = jasmine.createSpy('b').andReturn(bd.promise);
|
||||
var cd = $q.defer(), c = jasmine.createSpy('c').andReturn(cd.promise);
|
||||
|
||||
var r = $r.resolve({ a: [ 'b', 'c', a ], b: [ 'c', b ], c: [ c ] });
|
||||
tick();
|
||||
expect(r).not.toBeResolved();
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
expect(b).not.toHaveBeenCalled();
|
||||
expect(c).toHaveBeenCalled();
|
||||
cd.resolve('cc');
|
||||
tick();
|
||||
expect(r).not.toBeResolved();
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
expect(b).toHaveBeenCalled();
|
||||
expect(b.mostRecentCall.args).toEqual([ 'cc' ]);
|
||||
bd.resolve('bb');
|
||||
tick();
|
||||
expect(r).not.toBeResolved();
|
||||
expect(a).toHaveBeenCalled();
|
||||
expect(a.mostRecentCall.args).toEqual([ 'bb', 'cc' ]);
|
||||
ad.resolve('aa');
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual({ a: 'aa', b: 'bb', c: 'cc' });
|
||||
expect(a.callCount).toBe(1);
|
||||
expect(b.callCount).toBe(1);
|
||||
expect(c.callCount).toBe(1);
|
||||
}));
|
||||
|
||||
it("refuses cyclic dependencies", function () {
|
||||
var a = jasmine.createSpy('a');
|
||||
var b = jasmine.createSpy('b');
|
||||
expect(caught(function () {
|
||||
$r.resolve({ a: [ 'b', a ], b: [ 'a', b ] });
|
||||
})).toMatch(/cyclic/i);
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
expect(b).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows a function to depend on an injector value of the same name", function () {
|
||||
var r = $r.resolve({ $resolve: function($resolve) { return $resolve === $r; } });
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual({ $resolve: true });
|
||||
});
|
||||
|
||||
it("allows locals to be passed that override the injector", function () {
|
||||
var fun = jasmine.createSpy('fun');
|
||||
$r.resolve({ fun: [ '$resolve', fun ] }, { $resolve: 42 });
|
||||
tick();
|
||||
expect(fun).toHaveBeenCalled();
|
||||
expect(fun.mostRecentCall.args[0]).toBe(42);
|
||||
});
|
||||
|
||||
it("does not call injectables overridden by a local", function () {
|
||||
var fun = jasmine.createSpy('fun').andReturn("function");
|
||||
var r = $r.resolve({ fun: [ fun ] }, { fun: "local" });
|
||||
tick();
|
||||
expect(fun).not.toHaveBeenCalled();
|
||||
expect(resolvedValue(r)).toEqual({ fun: "local" });
|
||||
});
|
||||
|
||||
it("includes locals in the returned values", function () {
|
||||
var locals = { foo: 'hi', bar: 'mom' };
|
||||
var r = $r.resolve({}, locals);
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual(locals);
|
||||
});
|
||||
|
||||
it("allows inheritance from a parent resolve()", function () {
|
||||
var r = $r.resolve({ fun: function () { return true; } });
|
||||
var s = $r.resolve({ games: function () { return true; } }, r);
|
||||
tick();
|
||||
expect(r).toBeResolved();
|
||||
expect(resolvedValue(s)).toEqual({ fun: true, games: true });
|
||||
});
|
||||
|
||||
it("only accepts promises from $resolve as parent", inject(function ($q) {
|
||||
expect(caught(function () {
|
||||
$r.resolve({}, null, $q.defer().promise);
|
||||
})).toMatch(/\$resolve\.resolve/);
|
||||
}));
|
||||
|
||||
it("resolves dependencies from a parent resolve()", function () {
|
||||
var r = $r.resolve({ a: [ function() { return 'aa' } ] });
|
||||
var b = jasmine.createSpy('b');
|
||||
var s = $r.resolve({ b: [ 'a', b ] }, r);
|
||||
tick();
|
||||
expect(b).toHaveBeenCalled();
|
||||
expect(b.mostRecentCall.args).toEqual([ 'aa' ]);
|
||||
});
|
||||
|
||||
it("allows a function to override a parent value of the same name", function () {
|
||||
var r = $r.resolve({ b: function() { return 'B' } });
|
||||
var s = $r.resolve({
|
||||
a: function (b) { return 'a:' + b },
|
||||
b: function (b) { return '(' + b + ')' },
|
||||
c: function (b) { return 'c:' + b }
|
||||
}, r);
|
||||
tick();
|
||||
expect(resolvedValue(s)).toEqual({ a: 'a:(B)', b:'(B)', c:'c:(B)' });
|
||||
});
|
||||
|
||||
it("allows a function to override a parent value of the same name with a promise", inject(function ($q) {
|
||||
var r = $r.resolve({ b: function() { return 'B' } });
|
||||
var superb, bd = $q.defer();
|
||||
var s = $r.resolve({
|
||||
a: function (b) { return 'a:' + b },
|
||||
b: function (b) { superb = b; return bd.promise },
|
||||
c: function (b) { return 'c:' + b }
|
||||
}, r);
|
||||
tick();
|
||||
bd.resolve('(' + superb + ')');
|
||||
tick();
|
||||
expect(resolvedValue(s)).toEqual({ a: 'a:(B)', b:'(B)', c:'c:(B)' });
|
||||
}));
|
||||
|
||||
it("it only resolves after the parent resolves", inject(function ($q) {
|
||||
var bd = $q.defer(), b = jasmine.createSpy('b').andReturn(bd.promise);
|
||||
var cd = $q.defer(), c = jasmine.createSpy('c').andReturn(cd.promise);
|
||||
var r = $r.resolve({ c: [ c ] });
|
||||
var s = $r.resolve({ b: [ b ] }, r);
|
||||
bd.resolve('bbb');
|
||||
tick();
|
||||
expect(r).not.toBeResolved();
|
||||
expect(s).not.toBeResolved();
|
||||
cd.resolve('ccc');
|
||||
tick();
|
||||
expect(resolvedValue(r)).toEqual({ c: 'ccc' });
|
||||
expect(resolvedValue(s)).toEqual({ b: 'bbb', c: 'ccc' });
|
||||
}));
|
||||
|
||||
it("invokes functions as soon as possible", inject(function ($q) {
|
||||
var ad = $q.defer(), a = jasmine.createSpy('a').andReturn(ad.promise);
|
||||
var bd = $q.defer(), b = jasmine.createSpy('b').andReturn(bd.promise);
|
||||
var cd = $q.defer(), c = jasmine.createSpy('c').andReturn(cd.promise);
|
||||
|
||||
var r = $r.resolve({ c: [ c ] });
|
||||
var s = $r.resolve({ a: [ a ], b: [ 'c', b ] }, r);
|
||||
expect(c).toHaveBeenCalled(); // synchronously
|
||||
expect(a).toHaveBeenCalled(); // synchronously
|
||||
expect(r).not.toBeResolved();
|
||||
expect(s).not.toBeResolved();
|
||||
cd.resolve('ccc');
|
||||
tick();
|
||||
expect(b).toHaveBeenCalled();
|
||||
expect(b.mostRecentCall.args).toEqual([ 'ccc' ]);
|
||||
}));
|
||||
|
||||
it("passes the specified 'self' argument as 'this'", function () {
|
||||
var self = {}, passed;
|
||||
$r.resolve({ fun: function () { passed = this; } }, null, null, self);
|
||||
tick();
|
||||
expect(passed).toBe(self);
|
||||
});
|
||||
|
||||
it("rejects missing dependencies but does not fail synchronously", function () {
|
||||
var r = $r.resolve({ fun: function (invalid) {} });
|
||||
expect(r).not.toBeResolved();
|
||||
tick();
|
||||
expect(resolvedError(r)).toMatch(/unknown provider/i);
|
||||
});
|
||||
|
||||
it("propagates exceptions thrown by the functions as a rejection", function () {
|
||||
var r = $r.resolve({ fun: function () { throw "i want cake" } });
|
||||
expect(r).not.toBeResolved();
|
||||
tick();
|
||||
expect(resolvedError(r)).toBe("i want cake");
|
||||
});
|
||||
|
||||
it("propagates errors from a parent resolve", function () {
|
||||
var error = [ "the cake is a lie" ];
|
||||
var r = $r.resolve({ foo: function () { throw error } });
|
||||
var s = $r.resolve({ bar: function () { 42 } }, r);
|
||||
tick();
|
||||
expect(resolvedError(r)).toBe(error);
|
||||
expect(resolvedError(s)).toBe(error);
|
||||
});
|
||||
|
||||
it("does not invoke any functions if the parent resolve has already failed", function () {
|
||||
var r = $r.resolve({ foo: function () { throw "oops" } });
|
||||
tick();
|
||||
expect(r).toBeResolved();
|
||||
var a = jasmine.createSpy('a');
|
||||
var s = $r.resolve({ a: [ a ] }, r);
|
||||
tick();
|
||||
expect(resolvedError(s)).toBeDefined();
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not invoke any more functions after a failure", inject(function ($q) {
|
||||
var ad = $q.defer(), a = jasmine.createSpy('a').andReturn(ad.promise);
|
||||
var cd = $q.defer(), c = jasmine.createSpy('c').andReturn(cd.promise);
|
||||
var dd = $q.defer(), d = jasmine.createSpy('d').andReturn(dd.promise);
|
||||
var r = $r.resolve({ a: [ 'c', a ], c: [ c ], d: [ d ] });
|
||||
dd.reject('dontlikeit');
|
||||
tick();
|
||||
expect(resolvedError(r)).toBeDefined();
|
||||
cd.resolve('ccc');
|
||||
tick();
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it("does not invoke any more functions after a parent failure", inject(function ($q) {
|
||||
var ad = $q.defer(), a = jasmine.createSpy('a').andReturn(ad.promise);
|
||||
var cd = $q.defer(), c = jasmine.createSpy('c').andReturn(cd.promise);
|
||||
var dd = $q.defer(), d = jasmine.createSpy('d').andReturn(dd.promise);
|
||||
var r = $r.resolve({ c: [ c ], d: [ d ] });
|
||||
var s = $r.resolve({ a: [ 'c', a ] }, r);
|
||||
dd.reject('dontlikeit');
|
||||
tick();
|
||||
expect(resolvedError(r)).toBeDefined();
|
||||
expect(resolvedError(s)).toBeDefined();
|
||||
cd.resolve('ccc');
|
||||
tick();
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
describe(".study()", function () {
|
||||
it("returns a resolver function", function () {
|
||||
expect(typeof $r.study({})).toBe('function');
|
||||
});
|
||||
|
||||
it("refuses cyclic dependencies", function () {
|
||||
var a = jasmine.createSpy('a');
|
||||
var b = jasmine.createSpy('b');
|
||||
expect(caught(function () {
|
||||
$r.study({ a: [ 'b', a ], b: [ 'a', b ] });
|
||||
})).toMatch(/cyclic/i);
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
expect(b).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not call the injectables", function () {
|
||||
var a = jasmine.createSpy('a');
|
||||
var b = jasmine.createSpy('b');
|
||||
$r.study({ a: [ 'b', a ], b: [ b ] });
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
expect(b).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns a function that can be used multiple times", function () {
|
||||
var trace = [];
|
||||
var r = $r.study({ a: [ 'what', function (what) { trace.push("a: " + what) } ] });
|
||||
r({ what: '1' });
|
||||
expect(trace).toEqual([ 'a: 1' ]);
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
describe('uiStateRef', function() {
|
||||
|
||||
var el, template, scope, document;
|
||||
|
||||
beforeEach(module('ui.router'));
|
||||
|
||||
beforeEach(module(function($stateProvider) {
|
||||
$stateProvider.state('index', {
|
||||
url: '/'
|
||||
}).state('contacts', {
|
||||
url: '/contacts',
|
||||
template: '<a ui-sref=".item({ id: 5 })" class="item">Person</a> <ui-view></ui-view>'
|
||||
}).state('contacts.item', {
|
||||
url: '/:id',
|
||||
template: '<a ui-sref=".detail" class="item-detail">Detail</a> | <a ui-sref="^" class="item-parent">Parent</a> | <ui-view></ui-view>'
|
||||
}).state('contacts.item.detail', {
|
||||
template: '<div class="title">Detail</div> | <a ui-sref="^" class="item-parent2">Item</a>'
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($document) {
|
||||
document = $document[0];
|
||||
}));
|
||||
|
||||
function triggerClick(el, options) {
|
||||
options = angular.extend({
|
||||
metaKey: false,
|
||||
ctrlKey: false,
|
||||
shiftKey: false,
|
||||
altKey: false,
|
||||
button: 0
|
||||
}, options || {});
|
||||
|
||||
var e = document.createEvent("MouseEvents");
|
||||
e.initMouseEvent(
|
||||
"click", // typeArg of type DOMString, Specifies the event type.
|
||||
true, // canBubbleArg of type boolean, Specifies whether or not the event can bubble.
|
||||
true, // cancelableArg of type boolean, Specifies whether or not the event's default action can be prevented.
|
||||
undefined, // viewArg of type views::AbstractView, Specifies the Event's AbstractView.
|
||||
0, // detailArg of type long, Specifies the Event's mouse click count.
|
||||
0, // screenXArg of type long, Specifies the Event's screen x coordinate
|
||||
0, // screenYArg of type long, Specifies the Event's screen y coordinate
|
||||
0, // clientXArg of type long, Specifies the Event's client x coordinate
|
||||
0, // clientYArg of type long, Specifies the Event's client y coordinate
|
||||
options.ctrlKey, // ctrlKeyArg of type boolean, Specifies whether or not control key was depressed during the Event.
|
||||
options.altKey, // altKeyArg of type boolean, Specifies whether or not alt key was depressed during the Event.
|
||||
options.shiftKey, // shiftKeyArg of type boolean, Specifies whether or not shift key was depressed during the Event.
|
||||
options.metaKey, // metaKeyArg of type boolean, Specifies whether or not meta key was depressed during the Event.
|
||||
options.button, // buttonArg of type unsigned short, Specifies the Event's mouse button.
|
||||
null // relatedTargetArg of type EventTarget
|
||||
);
|
||||
el[0].dispatchEvent(e);
|
||||
}
|
||||
|
||||
describe('links with promises', function() {
|
||||
it('should update the href when promises on parameters change before scope is applied', inject(function($rootScope, $compile, $q) {
|
||||
promise = $q.defer()
|
||||
el = angular.element('<a ui-sref="contacts.item.detail({ id: contact.id })">Details</a>');
|
||||
scope = $rootScope;
|
||||
scope.contact = promise.promise;
|
||||
promise.resolve({id: 6});
|
||||
scope.$apply();
|
||||
$compile(el)(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(el.attr('href')).toBe('#/contacts/6');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('links', function() {
|
||||
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
el = angular.element('<a ui-sref="contacts.item.detail({ id: contact.id })">Details</a>');
|
||||
scope = $rootScope;
|
||||
scope.contact = { id: 5 };
|
||||
scope.$apply();
|
||||
|
||||
$compile(el)(scope);
|
||||
scope.$digest();
|
||||
}));
|
||||
|
||||
it('should generate the correct href', function() {
|
||||
expect(el.attr('href')).toBe('#/contacts/5');
|
||||
});
|
||||
|
||||
it('should update the href when parameters change', function() {
|
||||
expect(el.attr('href')).toBe('#/contacts/5');
|
||||
scope.contact.id = 6;
|
||||
scope.$apply();
|
||||
expect(el.attr('href')).toBe('#/contacts/6');
|
||||
});
|
||||
|
||||
it('should allow multi-line attribute values', inject(function($compile, $rootScope) {
|
||||
el = angular.element("<a ui-sref=\"contacts.item.detail({\n\tid: $index\n})\">Details</a>");
|
||||
$rootScope.$index = 3;
|
||||
$rootScope.$apply();
|
||||
|
||||
$compile(el)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(el.attr('href')).toBe('#/contacts/3');
|
||||
}));
|
||||
|
||||
it('should transition states when left-clicked', inject(function($state, $stateParams, $document, $q, $timeout) {
|
||||
expect($state.$current.name).toEqual('');
|
||||
|
||||
triggerClick(el);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
|
||||
expect($state.current.name).toEqual('contacts.item.detail');
|
||||
expect($stateParams).toEqual({ id: "5" });
|
||||
}));
|
||||
|
||||
it('should not transition states when ctrl-clicked', inject(function($state, $stateParams, $document, $q) {
|
||||
expect($state.$current.name).toEqual('');
|
||||
triggerClick(el, { ctrlKey: true });
|
||||
|
||||
$q.flush();
|
||||
expect($state.current.name).toEqual('');
|
||||
expect($stateParams).toEqual({ id: "5" });
|
||||
}));
|
||||
|
||||
it('should not transition states when meta-clicked', inject(function($state, $stateParams, $document, $q) {
|
||||
expect($state.$current.name).toEqual('');
|
||||
|
||||
triggerClick(el, { metaKey: true });
|
||||
$q.flush();
|
||||
|
||||
expect($state.current.name).toEqual('');
|
||||
expect($stateParams).toEqual({ id: "5" });
|
||||
}));
|
||||
|
||||
it('should not transition states when shift-clicked', inject(function($state, $stateParams, $document, $q) {
|
||||
expect($state.$current.name).toEqual('');
|
||||
|
||||
triggerClick(el, { shiftKey: true });
|
||||
$q.flush();
|
||||
|
||||
expect($state.current.name).toEqual('');
|
||||
expect($stateParams).toEqual({ id: "5" });
|
||||
}));
|
||||
|
||||
it('should not transition states when middle-clicked', inject(function($state, $stateParams, $document, $q) {
|
||||
expect($state.$current.name).toEqual('');
|
||||
|
||||
triggerClick(el, { button: 1 });
|
||||
$q.flush();
|
||||
|
||||
expect($state.current.name).toEqual('');
|
||||
expect($stateParams).toEqual({ id: "5" });
|
||||
}));
|
||||
});
|
||||
|
||||
describe('forms', function() {
|
||||
var el, scope;
|
||||
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
el = angular.element('<form ui-sref="contacts.item.detail({ id: contact.id })"></form>');
|
||||
scope = $rootScope;
|
||||
scope.contact = { id: 5 };
|
||||
scope.$apply();
|
||||
|
||||
$compile(el)(scope);
|
||||
scope.$digest();
|
||||
}));
|
||||
|
||||
it('should generate the correct action', function() {
|
||||
expect(el.attr('action')).toBe('#/contacts/5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative transitions', function() {
|
||||
|
||||
beforeEach(inject(function($rootScope, $compile, $state) {
|
||||
$state.transitionTo("contacts.item", { id: 5 });
|
||||
el = angular.element('<a ui-sref=".detail">Details</a>');
|
||||
scope = $rootScope;
|
||||
scope.$apply();
|
||||
|
||||
$compile(el)(scope);
|
||||
template = $compile(angular.element('<ui-view></ui-view>'))(scope);
|
||||
scope.$digest();
|
||||
}));
|
||||
|
||||
it('should work', inject(function ($state, $stateParams, $q, $timeout) {
|
||||
triggerClick(el);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
|
||||
expect($state.$current.name).toBe("contacts.item.detail");
|
||||
expect($state.params).toEqual({ id: '5' });
|
||||
}));
|
||||
|
||||
it('should resolve states from parent uiView', inject(function ($state, $stateParams, $q, $timeout) {
|
||||
$state.transitionTo('contacts');
|
||||
$q.flush();
|
||||
|
||||
var parentToChild = angular.element(template[0].querySelector('a.item'));
|
||||
triggerClick(parentToChild);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
|
||||
var childToGrandchild = angular.element(template[0].querySelector('a.item-detail'));
|
||||
var childToParent = angular.element(template[0].querySelector('a.item-parent'));
|
||||
|
||||
triggerClick(childToGrandchild);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
|
||||
var grandchildToParent = angular.element(template[0].querySelector('a.item-parent2'));
|
||||
expect($state.$current.name).toBe("contacts.item.detail")
|
||||
|
||||
triggerClick(grandchildToParent);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
expect($state.$current.name).toBe("contacts.item");
|
||||
|
||||
$state.transitionTo("contacts.item.detail", { id: 3 });
|
||||
triggerClick(childToParent);
|
||||
$timeout.flush();
|
||||
$q.flush();
|
||||
expect($state.$current.name).toBe("contacts");
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('uiSrefActive', function() {
|
||||
var el, template, scope, document;
|
||||
|
||||
beforeEach(module('ui.router'));
|
||||
|
||||
beforeEach(module(function($stateProvider) {
|
||||
$stateProvider.state('index', {
|
||||
url: '',
|
||||
}).state('contacts', {
|
||||
url: '/contacts',
|
||||
views: {
|
||||
'@': {
|
||||
template: '<a ui-sref=".item({ id: 6 })" ui-sref-active="active">Contacts</a>'
|
||||
}
|
||||
}
|
||||
}).state('contacts.item', {
|
||||
url: '/:id',
|
||||
}).state('contacts.item.detail', {
|
||||
url: '/detail/:foo'
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($document) {
|
||||
document = $document[0];
|
||||
}));
|
||||
|
||||
it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state) {
|
||||
el = angular.element('<div><a ui-sref="contacts" ui-sref-active="active">Contacts</a></div>');
|
||||
template = $compile(el)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
|
||||
$state.transitionTo('contacts');
|
||||
$q.flush();
|
||||
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
|
||||
|
||||
$state.transitionTo('contacts.item', { id: 5 });
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
|
||||
}));
|
||||
|
||||
it('should match state\'s parameters', inject(function($rootScope, $q, $compile, $state) {
|
||||
el = angular.element('<div><a ui-sref="contacts.item.detail({ foo: \'bar\' })" ui-sref-active="active">Contacts</a></div>');
|
||||
template = $compile(el)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
|
||||
$state.transitionTo('contacts.item.detail', { id: 5, foo: 'bar' });
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
|
||||
|
||||
$state.transitionTo('contacts.item.detail', { id: 5, foo: 'baz' });
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
|
||||
}));
|
||||
|
||||
it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state) {
|
||||
el = angular.element('<section><div ui-view></div></section>');
|
||||
template = $compile(el)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
$state.transitionTo('contacts');
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');
|
||||
|
||||
$state.transitionTo('contacts.item', { id: 6 });
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope active');
|
||||
|
||||
$state.transitionTo('contacts.item', { id: 5 });
|
||||
$q.flush();
|
||||
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
describe('isState filter', function() {
|
||||
beforeEach(module('ui.router'));
|
||||
beforeEach(module(function($stateProvider) {
|
||||
$stateProvider
|
||||
.state('a', { url: '/' })
|
||||
.state('a.b', { url: '/b' });
|
||||
}));
|
||||
|
||||
it('should return true if the current state exactly matches the input state', inject(function($parse, $state, $q, $rootScope) {
|
||||
$state.go('a');
|
||||
$q.flush();
|
||||
expect($parse('"a" | isState')($rootScope)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should return false if the current state does not exactly match the input state', inject(function($parse, $q, $state, $rootScope) {
|
||||
$state.go('a.b');
|
||||
$q.flush();
|
||||
expect($parse('"a" | isState')($rootScope)).toBe(false);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('includedByState filter', function() {
|
||||
beforeEach(module('ui.router'));
|
||||
beforeEach(module(function($stateProvider) {
|
||||
$stateProvider
|
||||
.state('a', { url: '/' })
|
||||
.state('a.b', { url: '/b' })
|
||||
.state('c', { url: '/c' });
|
||||
}));
|
||||
|
||||
it('should return true if the current state exactly matches the input state', inject(function($parse, $state, $q, $rootScope) {
|
||||
$state.go('a');
|
||||
$q.flush();
|
||||
expect($parse('"a" | includedByState')($rootScope)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should return true if the current state includes the input state', inject(function($parse, $state, $q, $rootScope) {
|
||||
$state.go('a.b');
|
||||
$q.flush();
|
||||
expect($parse('"a" | includedByState')($rootScope)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should return false if the current state does not include input state', inject(function($parse, $state, $q, $rootScope) {
|
||||
$state.go('c');
|
||||
$q.flush();
|
||||
expect($parse('"a" | includedByState')($rootScope)).toBe(false);
|
||||
}));
|
||||
});
|
||||
+928
@@ -0,0 +1,928 @@
|
||||
describe('state', function () {
|
||||
|
||||
var stateProvider, locationProvider, templateParams, ctrlName;
|
||||
|
||||
beforeEach(module('ui.router', function($locationProvider) {
|
||||
locationProvider = $locationProvider;
|
||||
$locationProvider.html5Mode(false);
|
||||
}));
|
||||
|
||||
var log, logEvents, logEnterExit;
|
||||
function eventLogger(event, to, toParams, from, fromParams) {
|
||||
if (logEvents) log += event.name + '(' + to.name + ',' + from.name + ');';
|
||||
}
|
||||
function callbackLogger(what) {
|
||||
return function () {
|
||||
if (logEnterExit) log += this.name + '.' + what + ';';
|
||||
};
|
||||
}
|
||||
|
||||
var A = { data: {} },
|
||||
B = {},
|
||||
C = {},
|
||||
D = { params: [ 'x', 'y' ] },
|
||||
DD = { parent: D, params: [ 'x', 'y', 'z' ] },
|
||||
E = { params: [ 'i' ] },
|
||||
H = { data: {propA: 'propA', propB: 'propB'} },
|
||||
HH = { parent: H },
|
||||
HHH = {parent: HH, data: {propA: 'overriddenA', propC: 'propC'} },
|
||||
RS = { url: '^/search?term', reloadOnSearch: false },
|
||||
AppInjectable = {};
|
||||
|
||||
beforeEach(module(function ($stateProvider, $provide) {
|
||||
angular.forEach([ A, B, C, D, DD, E, H, HH, HHH ], function (state) {
|
||||
state.onEnter = callbackLogger('onEnter');
|
||||
state.onExit = callbackLogger('onExit');
|
||||
});
|
||||
stateProvider = $stateProvider;
|
||||
|
||||
$stateProvider
|
||||
.state('A', A)
|
||||
.state('B', B)
|
||||
.state('C', C)
|
||||
.state('D', D)
|
||||
.state('DD', DD)
|
||||
.state('E', E)
|
||||
.state('H', H)
|
||||
.state('HH', HH)
|
||||
.state('HHH', HHH)
|
||||
.state('RS', RS)
|
||||
|
||||
.state('home', { url: "/" })
|
||||
.state('home.item', { url: "front/:id" })
|
||||
.state('about', { url: "/about" })
|
||||
.state('about.person', { url: "/:person" })
|
||||
.state('about.person.item', { url: "/:id" })
|
||||
.state('about.sidebar', {})
|
||||
.state('about.sidebar.item', {
|
||||
url: "/:item",
|
||||
templateUrl: function(params) {
|
||||
templateParams = params;
|
||||
return "/templates/" + params.item + ".html";
|
||||
}
|
||||
})
|
||||
.state('dynamicController', {
|
||||
url: "/dynamic/:type",
|
||||
template: "test",
|
||||
controllerProvider: function($stateParams) {
|
||||
ctrlName = $stateParams.type + "Controller";
|
||||
return ctrlName;
|
||||
}
|
||||
})
|
||||
.state('home.redirect', {
|
||||
url: "redir",
|
||||
onEnter: function($state) {
|
||||
$state.transitionTo("about");
|
||||
}
|
||||
})
|
||||
.state('resolveFail', {
|
||||
url: "/resolve-fail",
|
||||
resolve: {
|
||||
badness: function($q) {
|
||||
return $q.reject("!");
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('resolveTimeout', {
|
||||
url: "/:foo",
|
||||
resolve: {
|
||||
value: function ($timeout) {
|
||||
return $timeout(function() { log += "Success!"; }, 1);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.state('first', { url: '^/first/subpath' })
|
||||
.state('second', { url: '^/second' });
|
||||
|
||||
$provide.value('AppInjectable', AppInjectable);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($rootScope) {
|
||||
log = '';
|
||||
logEvents = logEnterExit = false;
|
||||
$rootScope.$on('$stateChangeStart', eventLogger);
|
||||
$rootScope.$on('$stateChangeSuccess', eventLogger);
|
||||
$rootScope.$on('$stateChangeError', eventLogger);
|
||||
$rootScope.$on('$stateNotFound', eventLogger);
|
||||
}));
|
||||
|
||||
|
||||
function $get(what) {
|
||||
return jasmine.getEnv().currentSpec.$injector.get(what);
|
||||
}
|
||||
|
||||
function initStateTo(state, optionalParams) {
|
||||
var $state = $get('$state'), $q = $get('$q');
|
||||
$state.transitionTo(state, optionalParams || {});
|
||||
$q.flush();
|
||||
expect($state.current).toBe(state);
|
||||
}
|
||||
|
||||
describe('provider', function () {
|
||||
it ('should ignore Object properties when registering states', function () {
|
||||
expect(function() {
|
||||
stateProvider.state('toString', { url: "/to-string" });
|
||||
}).not.toThrow();
|
||||
expect(function() {
|
||||
stateProvider.state('watch', { url: "/watch" });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.transitionTo()', function () {
|
||||
it('returns a promise for the target state', inject(function ($state, $q) {
|
||||
var trans = $state.transitionTo(A, {});
|
||||
$q.flush();
|
||||
expect(resolvedValue(trans)).toBe(A);
|
||||
}));
|
||||
|
||||
it('allows transitions by name', inject(function ($state, $q) {
|
||||
$state.transitionTo('A', {});
|
||||
$q.flush();
|
||||
expect($state.current).toBe(A);
|
||||
}));
|
||||
|
||||
it('doesn\'t trigger state change if reloadOnSearch is false', inject(function ($state, $q, $location, $rootScope){
|
||||
initStateTo(RS);
|
||||
$location.search({term: 'hello'});
|
||||
var called;
|
||||
$rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
|
||||
called = true
|
||||
});
|
||||
$q.flush();
|
||||
expect($location.search()).toEqual({term: 'hello'});
|
||||
expect(called).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('ignores non-applicable state parameters', inject(function ($state, $q) {
|
||||
$state.transitionTo('A', { w00t: 'hi mom!' });
|
||||
$q.flush();
|
||||
expect($state.current).toBe(A);
|
||||
}));
|
||||
|
||||
it('triggers $stateChangeStart', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(E, { i: 'iii' });
|
||||
var called;
|
||||
$rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
|
||||
expect(from).toBe(E);
|
||||
expect(fromParams).toEqual({ i: 'iii' });
|
||||
expect(to).toBe(D);
|
||||
expect(toParams).toEqual({ x: '1', y: '2' });
|
||||
|
||||
expect($state.current).toBe(from); // $state not updated yet
|
||||
expect($state.params).toEqual(fromParams);
|
||||
called = true;
|
||||
});
|
||||
$state.transitionTo(D, { x: '1', y: '2' });
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(D);
|
||||
}));
|
||||
|
||||
it('can be cancelled by preventDefault() in $stateChangeStart', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(A);
|
||||
var called;
|
||||
$rootScope.$on('$stateChangeStart', function (ev) {
|
||||
ev.preventDefault();
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.transitionTo(B, {});
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(A);
|
||||
expect(resolvedError(promise)).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('triggers $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(E, { i: 'iii' });
|
||||
var called;
|
||||
$rootScope.$on('$stateNotFound', function (ev, redirect, from, fromParams) {
|
||||
expect(from).toBe(E);
|
||||
expect(fromParams).toEqual({ i: 'iii' });
|
||||
expect(redirect.to).toEqual('never_defined');
|
||||
expect(redirect.toParams).toEqual({ x: '1', y: '2' });
|
||||
|
||||
expect($state.current).toBe(from); // $state not updated yet
|
||||
expect($state.params).toEqual(fromParams);
|
||||
called = true;
|
||||
});
|
||||
var message;
|
||||
try {
|
||||
$state.transitionTo('never_defined', { x: '1', y: '2' });
|
||||
} catch(err) {
|
||||
message = err.message;
|
||||
}
|
||||
$q.flush();
|
||||
expect(message).toEqual('No such state \'never_defined\'');
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(E);
|
||||
}));
|
||||
|
||||
it('can be cancelled by preventDefault() in $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(A);
|
||||
var called;
|
||||
$rootScope.$on('$stateNotFound', function (ev) {
|
||||
ev.preventDefault();
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.transitionTo('never_defined', {});
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(A);
|
||||
expect(resolvedError(promise)).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('can be redirected in $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(A);
|
||||
var called;
|
||||
$rootScope.$on('$stateNotFound', function (ev, redirect) {
|
||||
redirect.to = D;
|
||||
redirect.toParams = { x: '1', y: '2' };
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.transitionTo('never_defined', { z: 3 });
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(D);
|
||||
expect($state.params).toEqual({ x: '1', y: '2' });
|
||||
}));
|
||||
|
||||
it('can lazy-define a state in $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(DD, { x: 1, y: 2, z: 3 });
|
||||
var called;
|
||||
$rootScope.$on('$stateNotFound', function (ev, redirect) {
|
||||
stateProvider.state(redirect.to, { parent: DD, params: [ 'x', 'y', 'z', 'w' ]});
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.go('DDD', { w: 4 });
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current.name).toEqual('DDD');
|
||||
expect($state.params).toEqual({ x: '1', y: '2', z: '3', w: '4' });
|
||||
}));
|
||||
|
||||
it('can defer a state transition in $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(A);
|
||||
var called;
|
||||
var deferred = $q.defer();
|
||||
$rootScope.$on('$stateNotFound', function (ev, redirect) {
|
||||
ev.retry = deferred.promise;
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.go('AA', { a: 1 });
|
||||
stateProvider.state('AA', { parent: A, params: [ 'a' ]});
|
||||
deferred.resolve();
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current.name).toEqual('AA');
|
||||
expect($state.params).toEqual({ a: '1' });
|
||||
}));
|
||||
|
||||
it('can defer and supersede a state transition in $stateNotFound', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(A);
|
||||
var called;
|
||||
var deferred = $q.defer();
|
||||
$rootScope.$on('$stateNotFound', function (ev, redirect) {
|
||||
ev.retry = deferred.promise;
|
||||
called = true;
|
||||
});
|
||||
var promise = $state.go('AA', { a: 1 });
|
||||
$state.go(B);
|
||||
stateProvider.state('AA', { parent: A, params: [ 'a' ]});
|
||||
deferred.resolve();
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toEqual(B);
|
||||
expect($state.params).toEqual({});
|
||||
}));
|
||||
|
||||
it('triggers $stateChangeSuccess', inject(function ($state, $q, $rootScope) {
|
||||
initStateTo(E, { i: 'iii' });
|
||||
var called;
|
||||
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
|
||||
expect(from).toBe(E);
|
||||
expect(fromParams).toEqual({ i: 'iii' });
|
||||
expect(to).toBe(D);
|
||||
expect(toParams).toEqual({ x: '1', y: '2' });
|
||||
|
||||
expect($state.current).toBe(to); // $state has been updated
|
||||
expect($state.params).toEqual(toParams);
|
||||
called = true;
|
||||
});
|
||||
$state.transitionTo(D, { x: '1', y: '2' });
|
||||
$q.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect($state.current).toBe(D);
|
||||
}));
|
||||
|
||||
it('does not trigger $stateChangeSuccess when suppressed, but changes 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' }, { notify: false });
|
||||
$q.flush();
|
||||
|
||||
expect(called).toBeFalsy();
|
||||
expect($state.current).toBe(D);
|
||||
}));
|
||||
|
||||
it('does not trigger $stateChangeSuccess when suppressed, but 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' }, { notify: 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
|
||||
expect(trans).toBeDefined(); // but we still get a valid promise
|
||||
$q.flush();
|
||||
expect(resolvedValue(trans)).toBe(A);
|
||||
expect($state.current).toBe(A);
|
||||
expect(log).toBe('');
|
||||
}));
|
||||
|
||||
it('aborts pending transitions (last call wins)', inject(function ($state, $q) {
|
||||
initStateTo(A);
|
||||
logEvents = true;
|
||||
|
||||
var superseded = $state.transitionTo(B, {});
|
||||
$state.transitionTo(C, {});
|
||||
$q.flush();
|
||||
expect($state.current).toBe(C);
|
||||
expect(resolvedError(superseded)).toBeTruthy();
|
||||
expect(log).toBe(
|
||||
'$stateChangeStart(B,A);' +
|
||||
'$stateChangeStart(C,A);' +
|
||||
'$stateChangeSuccess(C,A);');
|
||||
}));
|
||||
|
||||
it('aborts pending transitions even when going back to the current state', inject(function ($state, $q) {
|
||||
initStateTo(A);
|
||||
logEvents = true;
|
||||
|
||||
var superseded = $state.transitionTo(B, {});
|
||||
$state.transitionTo(A, {});
|
||||
$q.flush();
|
||||
expect($state.current).toBe(A);
|
||||
expect(resolvedError(superseded)).toBeTruthy();
|
||||
expect(log).toBe('$stateChangeStart(B,A);');
|
||||
}));
|
||||
|
||||
it('aborts pending transitions when aborted from callbacks', inject(function ($state, $q) {
|
||||
var superseded = $state.transitionTo('home.redirect');
|
||||
$q.flush();
|
||||
expect($state.current.name).toBe('about');
|
||||
}));
|
||||
|
||||
it('triggers onEnter and onExit callbacks', inject(function ($state, $q) {
|
||||
initStateTo(A);
|
||||
logEnterExit = true;
|
||||
$state.transitionTo(D, {}); $q.flush();
|
||||
log += $state.current.name + ';';
|
||||
$state.transitionTo(DD, {}); $q.flush();
|
||||
log += $state.current.name + ';';
|
||||
$state.transitionTo(A, {}); $q.flush();
|
||||
expect(log).toBe(
|
||||
'A.onExit;' +
|
||||
'D.onEnter;' +
|
||||
'D;' +
|
||||
'DD.onEnter;' +
|
||||
'DD;' +
|
||||
'DD.onExit;' +
|
||||
'D.onExit;' +
|
||||
'A.onEnter;');
|
||||
}));
|
||||
|
||||
it('doesn\'t transition to parent state when child has no URL', inject(function ($state, $q) {
|
||||
$state.transitionTo('about.sidebar'); $q.flush();
|
||||
expect($state.current.name).toEqual('about.sidebar');
|
||||
}));
|
||||
|
||||
it('notifies on failed relative state resolution', inject(function ($state, $q) {
|
||||
$state.transitionTo(DD);
|
||||
$q.flush();
|
||||
|
||||
var err = "Could not resolve '^.Z' from state 'DD'";
|
||||
expect(function() { $state.transitionTo("^.Z", null, { relative: $state.$current }); }).toThrow(err);
|
||||
}));
|
||||
|
||||
it('uses the controllerProvider to get controller dynamically', inject(function ($state, $q) {
|
||||
$state.transitionTo('dynamicController', { type: "Acme" });
|
||||
$q.flush();
|
||||
expect(ctrlName).toEqual("AcmeController");
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.go()', function () {
|
||||
it('transitions to a relative state', inject(function ($state, $q) {
|
||||
$state.transitionTo('about.person.item', { id: 5 }); $q.flush();
|
||||
$state.go('^.^.sidebar'); $q.flush();
|
||||
expect($state.$current.name).toBe('about.sidebar');
|
||||
|
||||
// Transitions to absolute state
|
||||
$state.go("home"); $q.flush();
|
||||
expect($state.$current.name).toBe('home');
|
||||
|
||||
|
||||
// Transition to a child state
|
||||
$state.go(".item", { id: 5 }); $q.flush();
|
||||
expect($state.$current.name).toBe('home.item');
|
||||
|
||||
// Transition to grandparent's sibling through root
|
||||
// (Equivalent to absolute transition, assuming the root is known).
|
||||
$state.go("^.^.about"); $q.flush();
|
||||
expect($state.$current.name).toBe('about');
|
||||
|
||||
// Transition to grandchild
|
||||
$state.go(".person.item", { person: "bob", id: 13 }); $q.flush();
|
||||
expect($state.$current.name).toBe('about.person.item');
|
||||
|
||||
// Transition to immediate parent
|
||||
$state.go("^"); $q.flush();
|
||||
expect($state.$current.name).toBe('about.person');
|
||||
|
||||
// Transition to sibling
|
||||
$state.go("^.sidebar"); $q.flush();
|
||||
expect($state.$current.name).toBe('about.sidebar');
|
||||
}));
|
||||
|
||||
it('keeps parameters from common ancestor states', inject(function ($state, $stateParams, $q) {
|
||||
$state.transitionTo('about.person', { person: 'bob' });
|
||||
$q.flush();
|
||||
|
||||
$state.go('.item', { id: 5 });
|
||||
$q.flush();
|
||||
|
||||
expect($state.$current.name).toBe('about.person.item');
|
||||
expect($stateParams).toEqual({ person: 'bob', id: '5' });
|
||||
|
||||
$state.go('^.^.sidebar');
|
||||
$q.flush();
|
||||
expect($state.$current.name).toBe('about.sidebar');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.reload()', function () {
|
||||
it('should reload the current state with the current parameters', inject(function ($state, $q, $timeout) {
|
||||
$state.transitionTo('resolveTimeout', { foo: "bar" });
|
||||
$q.flush();
|
||||
expect(log).toBe('');
|
||||
|
||||
$timeout.flush();
|
||||
expect(log).toBe('Success!');
|
||||
|
||||
$state.reload();
|
||||
$q.flush();
|
||||
$timeout.flush();
|
||||
expect(log).toBe('Success!Success!');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.is()', function () {
|
||||
it('should return true when the current state is passed', inject(function ($state, $q) {
|
||||
$state.transitionTo(A); $q.flush();
|
||||
expect($state.is(A)).toBe(true);
|
||||
expect($state.is('A')).toBe(true);
|
||||
expect($state.is(B)).toBe(false);
|
||||
}));
|
||||
|
||||
it('should return undefined when queried state does not exist', inject(function ($state) {
|
||||
expect($state.is('Z')).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should return true when the current state is passed with matching parameters', inject(function ($state, $q) {
|
||||
$state.transitionTo(D, {x: 'foo', y: 'bar'}); $q.flush();
|
||||
expect($state.is(D, {x: 'foo', y: 'bar'})).toBe(true);
|
||||
expect($state.is('D', {x: 'foo', y: 'bar'})).toBe(true);
|
||||
expect($state.is(D, {x: 'bar', y: 'foo'})).toBe(false);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.includes()', function () {
|
||||
it('should return true when the current state is passed', inject(function ($state, $q) {
|
||||
$state.transitionTo(A); $q.flush();
|
||||
expect($state.includes(A)).toBe(true);
|
||||
expect($state.includes('A')).toBe(true);
|
||||
expect($state.includes(B)).toBe(false);
|
||||
}));
|
||||
|
||||
it('should return true when the current state\'s parent is passed', inject(function ($state, $q) {
|
||||
$state.transitionTo('about.person.item'); $q.flush();
|
||||
expect($state.includes('about')).toBe(true);
|
||||
expect($state.includes('about.person')).toBe(true);
|
||||
expect($state.includes('about.sidebar')).toBe(false);
|
||||
}));
|
||||
|
||||
it('should return undefined when queried state does not exist', inject(function ($state) {
|
||||
expect($state.includes('Z')).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should return true when the current state is passed with partial matching parameters', inject(function ($state, $q) {
|
||||
$state.transitionTo(D, {x: 'foo', y: 'bar'}); $q.flush();
|
||||
expect($state.includes(D, {x: 'foo'})).toBe(true);
|
||||
expect($state.includes(D, {y: 'bar'})).toBe(true);
|
||||
expect($state.includes('D', {x: 'foo'})).toBe(true);
|
||||
expect($state.includes(D, {y: 'foo'})).toBe(false);
|
||||
}));
|
||||
|
||||
it('should return true when the current state is passed with partial matching parameters from state\'s parent', inject(function ($state, $q) {
|
||||
$state.transitionTo('about.person.item', {person: 'bob', id: 5}); $q.flush();
|
||||
expect($state.includes('about.person', {person: 'bob'})).toBe(true);
|
||||
expect($state.includes('about.person', {person: 'steve'})).toBe(false);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.current', function () {
|
||||
it('is always defined', inject(function ($state) {
|
||||
expect($state.current).toBeDefined();
|
||||
}));
|
||||
|
||||
it('updates asynchronously as the transitionTo() promise is resolved', inject(function ($state, $q) {
|
||||
var trans = $state.transitionTo(A, {});
|
||||
expect($state.current).not.toBe(A);
|
||||
$q.flush();
|
||||
expect($state.current).toBe(A);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('$current', function () {
|
||||
it('is always defined', inject(function ($state) {
|
||||
expect($state.$current).toBeDefined();
|
||||
}));
|
||||
|
||||
it('wraps the raw state object', inject(function ($state) {
|
||||
initStateTo(A);
|
||||
expect($state.$current.data).toBe(A.data); // 'data' is reserved for app use
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('.params', function () {
|
||||
it('is always defined', inject(function ($state) {
|
||||
expect($state.params).toBeDefined();
|
||||
expect(angular.isObject($state.params)).toBe(true);
|
||||
}));
|
||||
|
||||
it('contains the parameter values for the current state', inject(function ($state, $q) {
|
||||
initStateTo(D, { x: 'x value', z: 'invalid value' });
|
||||
expect($state.params).toEqual({ x: 'x value', y: null });
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('.transition', function () {
|
||||
it('is null when no transition is taking place', inject(function ($state, $q) {
|
||||
expect($state.transition).toBeNull();
|
||||
$state.transitionTo(A, {});
|
||||
$q.flush();
|
||||
expect($state.transition).toBeNull();
|
||||
}));
|
||||
|
||||
it('is the current transition', inject(function ($state, $q) {
|
||||
var trans = $state.transitionTo(A, {});
|
||||
expect($state.transition).toBe(trans);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('.href()', function () {
|
||||
it('aborts on un-navigable states', inject(function ($state) {
|
||||
expect($state.href("A")).toBeNull();
|
||||
expect($state.href("about.sidebar", null, { lossy: false })).toBeNull();
|
||||
}));
|
||||
|
||||
it('generates a parent state URL when lossy is true', inject(function ($state) {
|
||||
expect($state.href("about.sidebar", null, { lossy: true })).toEqual("#/about");
|
||||
}));
|
||||
|
||||
it('generates a URL without parameters', inject(function ($state) {
|
||||
expect($state.href("home")).toEqual("#/");
|
||||
expect($state.href("about", {})).toEqual("#/about");
|
||||
expect($state.href("about", { foo: "bar" })).toEqual("#/about");
|
||||
}));
|
||||
|
||||
it('generates a URL with parameters', inject(function ($state) {
|
||||
expect($state.href("about.person", { person: "bob" })).toEqual("#/about/bob");
|
||||
expect($state.href("about.person.item", { person: "bob", id: null })).toEqual("#/about/bob/");
|
||||
}));
|
||||
|
||||
it('generates absolute url when absolute is true', inject(function ($state) {
|
||||
expect($state.href("about.sidebar", null, { absolute: true })).toEqual("http://server/#/about");
|
||||
locationProvider.html5Mode(true);
|
||||
expect($state.href("about.sidebar", null, { absolute: true })).toEqual("http://server/about");
|
||||
}));
|
||||
|
||||
it('respects $locationProvider.hashPrefix()', inject(function ($state) {
|
||||
locationProvider.hashPrefix("!");
|
||||
expect($state.href("home")).toEqual("#!/");
|
||||
}));
|
||||
});
|
||||
|
||||
describe('.get()', function () {
|
||||
it("should return the state's config", inject(function ($state) {
|
||||
expect($state.get('home').url).toBe('/');
|
||||
expect($state.get('home.item').url).toBe('front/:id');
|
||||
expect($state.get('A')).toBe(A);
|
||||
expect($state.get('Z')).toBeNull();
|
||||
}));
|
||||
|
||||
it("should return all of the state's config", inject(function ($state) {
|
||||
var list = $state.get().sort(function(a, b) { return (a.name > b.name) - (b.name > a.name); });
|
||||
var names = [
|
||||
'', // implicit root state
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'DD',
|
||||
'E',
|
||||
'H',
|
||||
'HH',
|
||||
'HHH',
|
||||
'RS',
|
||||
'about',
|
||||
'about.person',
|
||||
'about.person.item',
|
||||
'about.sidebar',
|
||||
'about.sidebar.item',
|
||||
'dynamicController',
|
||||
'first',
|
||||
'home',
|
||||
'home.item',
|
||||
'home.redirect',
|
||||
'resolveFail',
|
||||
'resolveTimeout',
|
||||
'second'
|
||||
];
|
||||
expect(list.map(function(state) { return state.name; })).toEqual(names);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('url handling', function () {
|
||||
it('should transition to the same state with different parameters', inject(function ($state, $rootScope, $location) {
|
||||
$location.path("/about/bob");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
expect($state.params).toEqual({ person: "bob" });
|
||||
expect($state.current.name).toBe('about.person');
|
||||
|
||||
$location.path("/about/larry");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
expect($state.params).toEqual({ person: "larry" });
|
||||
expect($state.current.name).toBe('about.person');
|
||||
}));
|
||||
|
||||
it('preserve hash', inject(function($state, $rootScope, $location) {
|
||||
$location.path("/about/bob");
|
||||
$location.hash("frag");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
expect($state.params).toEqual({ person: "bob" });
|
||||
expect($state.current.name).toBe('about.person');
|
||||
expect($location.path()).toBe('/about/bob');
|
||||
expect($location.hash()).toBe('frag');
|
||||
}));
|
||||
|
||||
it('should correctly handle absolute urls', inject(function ($state, $rootScope, $location) {
|
||||
$location.path("/first/subpath");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
expect($state.current.name).toBe('first');
|
||||
|
||||
$state.transitionTo('second');
|
||||
$rootScope.$apply();
|
||||
expect($state.current.name).toBe('second');
|
||||
expect($location.path()).toBe('/second');
|
||||
}));
|
||||
|
||||
it('should ignore bad urls', inject(function ($state, $rootScope, $location) {
|
||||
$location.path("/first/second");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
expect($state.current.name).toBe('');
|
||||
}));
|
||||
|
||||
it('should revert to last known working url on state change failure', inject(function ($state, $rootScope, $location, $q) {
|
||||
$state.transitionTo("about");
|
||||
$q.flush();
|
||||
|
||||
$location.path("/resolve-fail");
|
||||
$rootScope.$broadcast("$locationChangeSuccess");
|
||||
$rootScope.$apply();
|
||||
|
||||
expect($state.current.name).toBe("about");
|
||||
}));
|
||||
|
||||
it('should replace browser history when "replace" enabled', inject(function ($state, $rootScope, $location, $q) {
|
||||
var originalReplaceFn = $location.replace, replaceWasCalled = false;
|
||||
|
||||
// @todo Replace this with a spy
|
||||
var decoratedReplaceFn = function() {
|
||||
replaceWasCalled = true;
|
||||
originalReplaceFn.call($location);
|
||||
};
|
||||
$location.replace = decoratedReplaceFn;
|
||||
|
||||
$state.transitionTo('about', {}, { location: 'replace' });
|
||||
$q.flush();
|
||||
|
||||
expect(replaceWasCalled).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should not replace history normally', inject(function ($state, $rootScope, $location, $q) {
|
||||
var originalReplaceFn = $location.replace, replaceWasCalled = false;
|
||||
|
||||
// @todo Replace with spy
|
||||
var decoratedReplaceFn = function() {
|
||||
replaceWasCalled = true;
|
||||
originalReplaceFn.call($location);
|
||||
};
|
||||
$location.replace = decoratedReplaceFn;
|
||||
|
||||
$state.transitionTo('about');
|
||||
$q.flush();
|
||||
|
||||
expect(replaceWasCalled).toEqual(false);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('default properties', function() {
|
||||
it('should always have a name', inject(function ($state, $q) {
|
||||
$state.transitionTo(A);
|
||||
$q.flush();
|
||||
expect($state.$current.name).toBe('A');
|
||||
expect($state.$current.toString()).toBe('A');
|
||||
}));
|
||||
|
||||
it('should always have a resolve object', inject(function ($state) {
|
||||
expect($state.$current.resolve).toEqual({});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('"data" property inheritance/override', function () {
|
||||
it('should stay immutable for if state doesn\'t have parent', inject(function ($state) {
|
||||
initStateTo(H);
|
||||
expect($state.current.name).toEqual('H');
|
||||
expect($state.current.data.propA).toEqual(H.data.propA);
|
||||
expect($state.current.data.propB).toEqual(H.data.propB);
|
||||
}));
|
||||
|
||||
it('should be inherited from parent if state doesn\'t define it', inject(function ($state) {
|
||||
initStateTo(HH);
|
||||
expect($state.current.name).toEqual('HH');
|
||||
expect($state.current.data.propA).toEqual(H.data.propA);
|
||||
expect($state.current.data.propB).toEqual(H.data.propB);
|
||||
}));
|
||||
|
||||
it('should be overridden/extended if state defines it', inject(function ($state) {
|
||||
initStateTo(HHH);
|
||||
expect($state.current.name).toEqual('HHH');
|
||||
expect($state.current.data.propA).toEqual(HHH.data.propA);
|
||||
expect($state.current.data.propB).toEqual(H.data.propB);
|
||||
expect($state.current.data.propB).toEqual(HH.data.propB);
|
||||
expect($state.current.data.propC).toEqual(HHH.data.propC);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('html5Mode compatibility', function() {
|
||||
|
||||
it('should generate non-hashbang URLs in HTML5 mode', inject(function ($state) {
|
||||
expect($state.href("about.person", { person: "bob" })).toEqual("#/about/bob");
|
||||
locationProvider.html5Mode(true);
|
||||
expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
|
||||
}));
|
||||
});
|
||||
|
||||
describe('default properties', function () {
|
||||
it('should always have a name', inject(function ($state, $q) {
|
||||
$state.transitionTo(A); $q.flush();
|
||||
expect($state.$current.name).toBe('A');
|
||||
expect($state.$current.toString()).toBe('A');
|
||||
}));
|
||||
|
||||
it('should always have a resolve object', inject(function ($state) {
|
||||
expect($state.$current.resolve).toEqual({});
|
||||
}));
|
||||
|
||||
it('should include itself and parent states', inject(function ($state, $q) {
|
||||
$state.transitionTo(DD); $q.flush();
|
||||
expect($state.$current.includes).toEqual({ '': true, D: true, DD: true });
|
||||
}));
|
||||
});
|
||||
|
||||
describe('template handling', function () {
|
||||
it('should inject $stateParams into templateUrl function', inject(function ($state, $q, $httpBackend) {
|
||||
$httpBackend.expectGET("/templates/foo.html").respond("200");
|
||||
$state.transitionTo('about.sidebar.item', { item: "foo" }); $q.flush();
|
||||
expect(templateParams).toEqual({ item: "foo" });
|
||||
}));
|
||||
});
|
||||
|
||||
describe('provider decorators', function () {
|
||||
|
||||
it('should return built-in decorators', function () {
|
||||
expect(stateProvider.decorator('parent')({ parent: A }).self.name).toBe("A");
|
||||
});
|
||||
|
||||
it('should allow built-in decorators to be overridden', inject(function ($state, $q) {
|
||||
stateProvider.decorator('data', function(state) {
|
||||
return angular.extend(state.data || {}, { foo: "bar" });
|
||||
});
|
||||
stateProvider.state('AA', { parent: A, data: { baz: "true" } });
|
||||
|
||||
$state.transitionTo('AA');
|
||||
$q.flush();
|
||||
expect($state.current.data).toEqual({ baz: 'true', foo: 'bar' });
|
||||
}));
|
||||
|
||||
it('should allow new decorators to be added', inject(function ($state, $q) {
|
||||
stateProvider.decorator('custom', function(state) {
|
||||
return function() { return "Custom functionality for state '" + state + "'" };
|
||||
});
|
||||
stateProvider.state('decoratorTest', {});
|
||||
|
||||
$state.transitionTo('decoratorTest');
|
||||
$q.flush();
|
||||
expect($state.$current.custom()).toBe("Custom functionality for state 'decoratorTest'");
|
||||
}));
|
||||
|
||||
it('should allow built-in decorators to be extended', inject(function ($state, $q, $httpBackend) {
|
||||
stateProvider.decorator('views', function(state, parent) {
|
||||
var result = {};
|
||||
|
||||
angular.forEach(parent(state), function(config, name) {
|
||||
result[name] = angular.extend(config, { templateProvider: function() {
|
||||
return "Template for " + name;
|
||||
}});
|
||||
});
|
||||
return result;
|
||||
});
|
||||
|
||||
stateProvider.state('viewTest', {
|
||||
views: {
|
||||
viewA: {},
|
||||
viewB: {}
|
||||
}
|
||||
});
|
||||
|
||||
$state.transitionTo('viewTest');
|
||||
$q.flush();
|
||||
|
||||
expect($state.$current.views['viewA@'].templateProvider()).toBe('Template for viewA@');
|
||||
expect($state.$current.views['viewB@'].templateProvider()).toBe('Template for viewB@');
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('state queue', function(){
|
||||
angular.module('ui.router.queue.test', ['ui.router.queue.test.dependency'])
|
||||
.config(function($stateProvider) {
|
||||
$stateProvider
|
||||
.state('queue-test-a', {})
|
||||
.state('queue-test-b-child', { parent: 'queue-test-b' })
|
||||
.state('queue-test-b', {});
|
||||
});
|
||||
angular.module('ui.router.queue.test.dependency', [])
|
||||
.config(function($stateProvider) {
|
||||
$stateProvider
|
||||
.state('queue-test-a.child', {})
|
||||
});
|
||||
|
||||
var expectedStates = ['','queue-test-a', 'queue-test-a.child', 'queue-test-b', 'queue-test-b-child'];
|
||||
|
||||
it('should work across modules', function() {
|
||||
module('ui.router.queue.test', 'ui.router.queue.test.dependency');
|
||||
|
||||
inject(function ($state) {
|
||||
var list = $state.get().sort(function(a, b) { return (a.name > b.name) - (b.name > a.name); });
|
||||
expect(list.map(function(state) { return state.name; })).toEqual(expectedStates);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work when parent is name string', function() {
|
||||
module('ui.router.queue.test', 'ui.router.queue.test.dependency');
|
||||
|
||||
inject(function ($state) {
|
||||
var list = $state.get().sort(function(a, b) { return (a.name > b.name) - (b.name > a.name); });
|
||||
expect(list.map(function(state) { return state.name; })).toEqual(expectedStates);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
describe('templateFactory', function () {
|
||||
|
||||
beforeEach(module('ui.router.util'));
|
||||
|
||||
it('exists', inject(function ($templateFactory) {
|
||||
expect($templateFactory).toBeDefined();
|
||||
}));
|
||||
});
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// Promise testing support
|
||||
angular.module('ngMock')
|
||||
.config(function ($provide) {
|
||||
$provide.decorator('$q', function ($delegate, $rootScope) {
|
||||
$delegate.flush = function() {
|
||||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
// Add callbacks to the promise that expose the resolved value/error
|
||||
function expose(promise) {
|
||||
// Don't add hooks to the same promise twice (shouldn't happen anyway)
|
||||
if (!promise.hasOwnProperty('$$resolved')) {
|
||||
promise.$$resolved = false;
|
||||
promise.then(function (value) {
|
||||
promise.$$resolved = { success: true, value: value };
|
||||
}, function (error) {
|
||||
promise.$$resolved = { success: false, error: error };
|
||||
});
|
||||
|
||||
// We need to expose() any then()ed promises recursively
|
||||
var qThen = promise.then;
|
||||
promise.then = function () {
|
||||
return expose(qThen.apply(this, arguments));
|
||||
};
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Wrap functions that return a promise
|
||||
angular.forEach([ 'when', 'all', 'reject'], function (name) {
|
||||
var qFunc = $delegate[name];
|
||||
$delegate[name] = function () {
|
||||
return expose(qFunc.apply(this, arguments));
|
||||
};
|
||||
});
|
||||
|
||||
// Wrap defer()
|
||||
var qDefer = $delegate.defer;
|
||||
$delegate.defer = function () {
|
||||
var deferred = qDefer();
|
||||
expose(deferred.promise);
|
||||
return deferred;
|
||||
}
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
|
||||
function testablePromise(promise) {
|
||||
if (!promise || !promise.then) throw new Error('Expected a promise, but got ' + jasmine.pp(promise) + '.');
|
||||
if (!isDefined(promise.$$resolved)) throw new Error('Promise has not been augmented by ngMock');
|
||||
return promise;
|
||||
}
|
||||
|
||||
function resolvedPromise(promise) {
|
||||
var result = testablePromise(promise).$$resolved;
|
||||
if (!result) throw new Error('Promise is not resolved yet');
|
||||
return result;
|
||||
}
|
||||
|
||||
function resolvedValue(promise) {
|
||||
var result = resolvedPromise(promise);
|
||||
if (!result.success) throw result.error;
|
||||
return result.value;
|
||||
}
|
||||
|
||||
function resolvedError(promise) {
|
||||
var result = resolvedPromise(promise);
|
||||
if (result.success) throw new Error('Promise was expected to fail but returned ' + jasmin.pp(res.value) + '.');
|
||||
return result.error;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
this.addMatchers({
|
||||
toBeResolved: function() {
|
||||
return !!testablePromise(this.actual).$$resolved;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Misc test utils
|
||||
function caught(fn) {
|
||||
try {
|
||||
fn();
|
||||
return null;
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// Utils for test from core angular
|
||||
var noop = angular.noop,
|
||||
toJson = angular.toJson;
|
||||
beforeEach(module('ui.router.compat'));
|
||||
@@ -0,0 +1,142 @@
|
||||
describe("UrlMatcher", function () {
|
||||
it("matches static URLs", function () {
|
||||
expect(new UrlMatcher('/hello/world').exec('/hello/world')).toEqual({});
|
||||
});
|
||||
|
||||
it("matches against the entire path", function () {
|
||||
var matcher = new UrlMatcher('/hello/world');
|
||||
expect(matcher.exec('/hello/world/')).toBeNull();
|
||||
expect(matcher.exec('/hello/world/suffix')).toBeNull();
|
||||
});
|
||||
|
||||
it("parses parameter placeholders", function () {
|
||||
var matcher = new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?from&to');
|
||||
var params = matcher.parameters();
|
||||
expect(params.length).toBe(5);
|
||||
expect(params).toContain('id');
|
||||
expect(params).toContain('type');
|
||||
expect(params).toContain('repeat');
|
||||
expect(params).toContain('from');
|
||||
expect(params).toContain('to');
|
||||
});
|
||||
|
||||
it("handles proper snake case parameter names", function(){
|
||||
var matcher = new UrlMatcher('/users/?from&to&snake-case&snake-case-triple');
|
||||
var params = matcher.parameters();
|
||||
expect(params.length).toBe(4);
|
||||
expect(params).toContain('from');
|
||||
expect(params).toContain('to');
|
||||
expect(params).toContain('snake-case');
|
||||
expect(params).toContain('snake-case-triple');
|
||||
});
|
||||
|
||||
it("handles invalid snake case parameter names", function(){
|
||||
expect(function() { new UrlMatcher('/users/?from&to&-snake'); }).toThrow(
|
||||
"Invalid parameter name '-snake' in pattern '/users/?from&to&-snake'"
|
||||
);
|
||||
|
||||
expect(function() { new UrlMatcher('/users/?from&to&snake-'); }).toThrow(
|
||||
"Invalid parameter name 'snake-' in pattern '/users/?from&to&snake-'"
|
||||
);
|
||||
});
|
||||
|
||||
it(".exec() captures parameter values", function () {
|
||||
expect(
|
||||
new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?from&to')
|
||||
.exec('/users/123/details//0', {}))
|
||||
.toEqual({ id:'123', type:'', repeat:'0' });
|
||||
});
|
||||
|
||||
it(".exec() captures catch-all parameters", function () {
|
||||
var m = new UrlMatcher('/document/*path');
|
||||
expect(m.exec('/document/a/b/c', {})).toEqual({ path: 'a/b/c' });
|
||||
expect(m.exec('/document/', {})).toEqual({ path: '' });
|
||||
});
|
||||
|
||||
it(".exec() uses the optional regexp with curly brace placeholders", function () {
|
||||
expect(
|
||||
new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?from&to')
|
||||
.exec('/users/123/details/what/thisShouldBeDigits', {}))
|
||||
.toBeNull();
|
||||
});
|
||||
|
||||
it(".exec() treats the URL as already decoded and does not decode it further", function () {
|
||||
expect(new UrlMatcher('/users/:id').exec('/users/100%25', {})).toEqual({ id: '100%25'});
|
||||
});
|
||||
|
||||
it('.exec() throws on unbalanced capture list', function () {
|
||||
var shouldThrow = {
|
||||
"/url/{matchedParam:([a-z]+)}/child/{childParam}": '/url/someword/child/childParam',
|
||||
"/url/{matchedParam:([a-z]+)}/child/{childParam}?foo": '/url/someword/child/childParam'
|
||||
};
|
||||
|
||||
angular.forEach(shouldThrow, function(url, route) {
|
||||
expect(function() { new UrlMatcher(route).exec(url, {}); }).toThrow(
|
||||
"Unbalanced capture group in route '" + route + "'"
|
||||
);
|
||||
});
|
||||
|
||||
var shouldPass = {
|
||||
"/url/{matchedParam:[a-z]+}/child/{childParam}": '/url/someword/child/childParam',
|
||||
"/url/{matchedParam:[a-z]+}/child/{childParam}?foo": '/url/someword/child/childParam'
|
||||
};
|
||||
|
||||
angular.forEach(shouldPass, function(url, route) {
|
||||
expect(function() { new UrlMatcher(route).exec(url, {}); }).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it(".format() reconstitutes the URL", function () {
|
||||
expect(
|
||||
new UrlMatcher('/users/:id/details/{type}/{repeat:[0-9]+}?from')
|
||||
.format({ id:'123', type:'default', repeat:444, ignored:'value', from:'1970' }))
|
||||
.toEqual('/users/123/details/default/444?from=1970');
|
||||
});
|
||||
|
||||
it(".format() encodes URL parameters", function () {
|
||||
expect(new UrlMatcher('/users/:id').format({ id:'100%'})).toEqual('/users/100%25');
|
||||
});
|
||||
|
||||
it(".format() encodes URL parameters with hashes", function () {
|
||||
expect(
|
||||
new UrlMatcher('/users/:id#:section')
|
||||
.format({ id: 'bob', section: 'contact-details' }))
|
||||
.toEqual('/users/bob#contact-details');
|
||||
});
|
||||
|
||||
it(".concat() concatenates matchers", function () {
|
||||
var matcher = new UrlMatcher('/users/:id/details/{type}?from').concat('/{repeat:[0-9]+}?to');
|
||||
var params = matcher.parameters();
|
||||
expect(params.length).toBe(5);
|
||||
expect(params).toContain('id');
|
||||
expect(params).toContain('type');
|
||||
expect(params).toContain('repeat');
|
||||
expect(params).toContain('from');
|
||||
expect(params).toContain('to');
|
||||
});
|
||||
|
||||
it(".concat() returns a new matcher", function () {
|
||||
var base = new UrlMatcher('/users/:id/details/{type}?from');
|
||||
var matcher = base.concat('/{repeat:[0-9]+}?to');
|
||||
expect(matcher).toNotBe(base);
|
||||
});
|
||||
});
|
||||
|
||||
describe("urlMatcherFactory", function () {
|
||||
|
||||
var $umf;
|
||||
|
||||
beforeEach(module('ui.router.util'));
|
||||
beforeEach(inject(function($urlMatcherFactory) {
|
||||
$umf = $urlMatcherFactory;
|
||||
}));
|
||||
|
||||
it("compiles patterns", function () {
|
||||
var matcher = $umf.compile('/hello/world');
|
||||
expect(matcher instanceof UrlMatcher).toBe(true);
|
||||
});
|
||||
|
||||
it("recognizes matchers", function () {
|
||||
expect($umf.isMatcher(new UrlMatcher('/'))).toBe(true);
|
||||
});
|
||||
});
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
describe("UrlRouter", function () {
|
||||
|
||||
var $urp, $ur, location, match, scope;
|
||||
|
||||
beforeEach(function() {
|
||||
angular.module('ui.router.router.test', function() {}).config(function ($urlRouterProvider) {
|
||||
$urp = $urlRouterProvider;
|
||||
|
||||
$urp.rule(function ($injector, $location) {
|
||||
var path = $location.path();
|
||||
if (!/baz/.test(path)) return false;
|
||||
return path.replace('baz', 'b4z');
|
||||
}).when('/foo/:param', function($match) {
|
||||
match = ['/foo/:param', $match];
|
||||
}).when('/bar', function($match) {
|
||||
match = ['/bar', $match];
|
||||
});
|
||||
});
|
||||
|
||||
module('ui.router.router', 'ui.router.router.test');
|
||||
|
||||
inject(function($rootScope, $location, $injector) {
|
||||
scope = $rootScope.$new();
|
||||
location = $location;
|
||||
$ur = $injector.invoke($urp.$get);
|
||||
});
|
||||
});
|
||||
|
||||
describe("provider", function () {
|
||||
|
||||
it("should throw on non-function rules", function () {
|
||||
expect(function() { $urp.rule(null); }).toThrow("'rule' must be a function")
|
||||
expect(function() { $urp.otherwise(null); }).toThrow("'rule' must be a function")
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("service", function() {
|
||||
it("should execute rewrite rules", function () {
|
||||
location.path("/foo");
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
expect(location.path()).toBe("/foo");
|
||||
|
||||
location.path("/baz");
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
expect(location.path()).toBe("/b4z");
|
||||
});
|
||||
|
||||
it("should keep otherwise last", function () {
|
||||
$urp.otherwise('/otherwise');
|
||||
|
||||
location.path("/lastrule");
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
expect(location.path()).toBe("/otherwise");
|
||||
|
||||
$urp.when('/lastrule', function($match) {
|
||||
match = ['/lastrule', $match];
|
||||
});
|
||||
|
||||
location.path("/lastrule");
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
expect(location.path()).toBe("/lastrule");
|
||||
});
|
||||
|
||||
it("should allow custom URL matchers", function () {
|
||||
var custom = {
|
||||
url: { exec: function() {}, format: function() {}, concat: function() {} },
|
||||
handler: function() {}
|
||||
};
|
||||
|
||||
spyOn(custom.url, "exec").andReturn({});
|
||||
spyOn(custom.url, "format").andReturn("/foo-bar");
|
||||
spyOn(custom, "handler").andReturn(true);
|
||||
|
||||
$urp.when(custom.url, custom.handler);
|
||||
scope.$broadcast("$locationChangeSuccess");
|
||||
scope.$apply();
|
||||
|
||||
expect(custom.url.exec).toHaveBeenCalled();
|
||||
expect(custom.url.format).not.toHaveBeenCalled();
|
||||
expect(custom.handler).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can be cancelled by preventDefault() in $locationChangeSuccess', inject(function () {
|
||||
var called;
|
||||
location.path("/baz");
|
||||
scope.$on('$locationChangeSuccess', function (ev) {
|
||||
ev.preventDefault();
|
||||
called = true;
|
||||
});
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
expect(called).toBeTruthy();
|
||||
expect(location.path()).toBe("/baz");
|
||||
}));
|
||||
|
||||
it('can be deferred and updated in $locationChangeSuccess', inject(function ($urlRouter, $timeout) {
|
||||
var called;
|
||||
location.path("/baz");
|
||||
scope.$on('$locationChangeSuccess', function (ev) {
|
||||
ev.preventDefault();
|
||||
called = true;
|
||||
$timeout($urlRouter.sync, 2000);
|
||||
});
|
||||
scope.$emit("$locationChangeSuccess");
|
||||
$timeout.flush();
|
||||
expect(called).toBeTruthy();
|
||||
expect(location.path()).toBe("/b4z");
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/*jshint browser: true, indent: 2 */
|
||||
/*global describe: false, it: false, beforeEach: false, expect: false, resolvedValue: false, module: false, inject: false, angular: false */
|
||||
|
||||
/*innerText shim for Firefox */
|
||||
function innerText(elem) {
|
||||
if (/firefox/i.test(navigator.userAgent)) {
|
||||
return elem.textContent;
|
||||
} else {
|
||||
return elem.innerText;
|
||||
}
|
||||
}
|
||||
|
||||
describe('uiView', function () {
|
||||
'use strict';
|
||||
|
||||
var scope, $compile, elem;
|
||||
|
||||
beforeEach(module('ui.router'));
|
||||
|
||||
var aState = {
|
||||
template: 'aState template'
|
||||
},
|
||||
bState = {
|
||||
template: 'bState template'
|
||||
},
|
||||
cState = {
|
||||
views: {
|
||||
'cview': {
|
||||
template: 'cState cview template'
|
||||
}
|
||||
}
|
||||
},
|
||||
dState = {
|
||||
views: {
|
||||
'dview1': {
|
||||
template: 'dState dview1 template'
|
||||
},
|
||||
'dview2': {
|
||||
template: 'dState dview2 template'
|
||||
}
|
||||
}
|
||||
},
|
||||
eState = {
|
||||
template: '<div ui-view="eview" class="eview"></div>'
|
||||
},
|
||||
fState = {
|
||||
views: {
|
||||
'eview': {
|
||||
template: 'fState eview template'
|
||||
}
|
||||
}
|
||||
},
|
||||
gState = {
|
||||
template: '<div ui-view="inner"><span ng-class="{ test: true }">{{content}}</span></div>'
|
||||
},
|
||||
hState = {
|
||||
views: {
|
||||
'inner': {
|
||||
template: 'hState inner template'
|
||||
}
|
||||
}
|
||||
},
|
||||
iState = {
|
||||
template: '<div ui-view>'+
|
||||
'<ul><li ng-repeat="item in items">{{item}}</li></ul>'+
|
||||
'</div>'
|
||||
},
|
||||
jState = {
|
||||
template: '<span ng-class="test">jState</span>'
|
||||
};
|
||||
|
||||
beforeEach(module(function ($stateProvider) {
|
||||
$stateProvider
|
||||
.state('a', aState)
|
||||
.state('b', bState)
|
||||
.state('c', cState)
|
||||
.state('d', dState)
|
||||
.state('e', eState)
|
||||
.state('e.f', fState)
|
||||
.state('g', gState)
|
||||
.state('g.h', hState)
|
||||
.state('i', iState)
|
||||
.state('j', jState);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($rootScope, _$compile_) {
|
||||
scope = $rootScope.$new();
|
||||
$compile = _$compile_;
|
||||
elem = angular.element('<div>');
|
||||
}));
|
||||
|
||||
describe('linking ui-directive', function () {
|
||||
it('anonymous ui-view should be replaced with the template of the current $state', inject(function ($state, $q) {
|
||||
elem.append($compile('<div ui-view></div>')(scope));
|
||||
|
||||
$state.transitionTo(aState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe(aState.template);
|
||||
}));
|
||||
|
||||
it('named ui-view should be replaced with the template of the current $state', inject(function ($state, $q) {
|
||||
elem.append($compile('<div ui-view="cview"></div>')(scope));
|
||||
|
||||
$state.transitionTo(cState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe(cState.views.cview.template);
|
||||
}));
|
||||
|
||||
it('ui-view should be updated after transition to another state', inject(function ($state, $q) {
|
||||
elem.append($compile('<div ui-view></div>')(scope));
|
||||
|
||||
$state.transitionTo(aState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe(aState.template);
|
||||
|
||||
$state.transitionTo(bState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe(bState.template);
|
||||
}));
|
||||
|
||||
it('should handle NOT nested ui-views', inject(function ($state, $q) {
|
||||
elem.append($compile('<div ui-view="dview1" class="dview1"></div><div ui-view="dview2" class="dview2"></div>')(scope));
|
||||
|
||||
$state.transitionTo(dState);
|
||||
$q.flush();
|
||||
|
||||
expect(innerText(elem[0].querySelector('.dview1'))).toBe(dState.views.dview1.template);
|
||||
expect(innerText(elem[0].querySelector('.dview2'))).toBe(dState.views.dview2.template);
|
||||
}));
|
||||
|
||||
it('should handle nested ui-views (testing two levels deep)', inject(function ($state, $q) {
|
||||
elem.append($compile('<div ui-view class="view"></div>')(scope));
|
||||
|
||||
$state.transitionTo(fState);
|
||||
$q.flush();
|
||||
|
||||
expect(innerText(elem[0].querySelector('.view').querySelector('.eview'))).toBe(fState.views.eview.template);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('handling initial view', function () {
|
||||
it('initial view should be compiled if the view is empty', inject(function ($state, $q) {
|
||||
var content = 'inner content';
|
||||
|
||||
elem.append($compile('<div ui-view></div>')(scope));
|
||||
scope.$apply('content = "' + content + '"');
|
||||
|
||||
$state.transitionTo(gState);
|
||||
$q.flush();
|
||||
|
||||
expect(innerText(elem[0].querySelector('.test'))).toBe(content);
|
||||
}));
|
||||
|
||||
it('initial view should be put back after removal of the view', inject(function ($state, $q) {
|
||||
var content = 'inner content';
|
||||
|
||||
elem.append($compile('<div ui-view></div>')(scope));
|
||||
scope.$apply('content = "' + content + '"');
|
||||
|
||||
$state.transitionTo(hState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe(hState.views.inner.template);
|
||||
|
||||
// going to the parent state which makes the inner view empty
|
||||
$state.transitionTo(gState);
|
||||
$q.flush();
|
||||
|
||||
expect(innerText(elem[0].querySelector('.test'))).toBe(content);
|
||||
}));
|
||||
|
||||
// related to issue #435
|
||||
it('initial view should be transcluded once to prevent breaking other directives', inject(function ($state, $q) {
|
||||
scope.items = ["I", "am", "a", "list", "of", "items"];
|
||||
|
||||
elem.append($compile('<div ui-view></div>')(scope));
|
||||
|
||||
// transition to state that has an initial view
|
||||
$state.transitionTo(iState);
|
||||
$q.flush();
|
||||
|
||||
// verify if ng-repeat has been compiled
|
||||
expect(elem.find('li').length).toBe(scope.items.length);
|
||||
|
||||
// transition to another state that replace the initial content
|
||||
$state.transitionTo(jState);
|
||||
$q.flush();
|
||||
|
||||
expect(elem.text()).toBe('jState');
|
||||
|
||||
// transition back to the state with empty subview and the initial view
|
||||
$state.transitionTo(iState);
|
||||
$q.flush();
|
||||
|
||||
// verify if the initial view is correct
|
||||
expect(elem.find('li').length).toBe(scope.items.length);
|
||||
|
||||
// change scope properties
|
||||
scope.$apply(function () {
|
||||
scope.items.push(".", "Working?");
|
||||
});
|
||||
|
||||
// verify if the initial view has been updated
|
||||
expect(elem.find('li').length).toBe(scope.items.length);
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "angular",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular.js",
|
||||
"dependencies": {},
|
||||
"homepage": "https://github.com/angular/bower-angular",
|
||||
"_release": "1.2.16",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.16",
|
||||
"commit": "7ae38b4a0cfced157e3486a0d6e2d299601723bb"
|
||||
},
|
||||
"_source": "git://github.com/angular/bower-angular.git",
|
||||
"_target": ">= 1.1.5",
|
||||
"_originalSource": "angular"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# bower-angular
|
||||
|
||||
This repo is for distribution on `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
Install with `bower`:
|
||||
|
||||
```shell
|
||||
bower install angular
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular/angular.js"></script>
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Include this file in your html if you are using the CSP mode. */
|
||||
|
||||
@charset "UTF-8";
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak,
|
||||
.ng-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ng-animate-block-transitions {
|
||||
transition:0s all!important;
|
||||
-webkit-transition:0s all!important;
|
||||
}
|
||||
Vendored
+21464
File diff suppressed because it is too large
Load Diff
Vendored
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
AngularJS v1.2.16
|
||||
(c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(O,U,s){'use strict';function t(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.2.16/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function ab(b){if(null==b||Ca(b))return!1;
|
||||
var a=b.length;return 1===b.nodeType&&a?!0:w(b)||M(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(P(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(ab(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Qb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Sc(b,
|
||||
a,c){for(var d=Qb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Rb(b){return function(a,c){b(c,a)}}function bb(){for(var b=ka.length,a;b;){b--;a=ka[b].charCodeAt(0);if(57==a)return ka[b]="A",ka.join("");if(90==a)ka[b]="0";else return ka[b]=String.fromCharCode(a+1),ka.join("")}ka.unshift("0");return ka.join("")}function Sb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function D(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});Sb(b,a);return b}function Y(b){return parseInt(b,
|
||||
10)}function Tb(b,a){return D(new (D(function(){},{prototype:b})),a)}function C(){}function Da(b){return b}function aa(b){return function(){return b}}function E(b){return"undefined"===typeof b}function B(b){return"undefined"!==typeof b}function X(b){return null!=b&&"object"===typeof b}function w(b){return"string"===typeof b}function vb(b){return"number"===typeof b}function Na(b){return"[object Date]"===wa.call(b)}function M(b){return"[object Array]"===wa.call(b)}function P(b){return"function"===typeof b}
|
||||
function cb(b){return"[object RegExp]"===wa.call(b)}function Ca(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function Tc(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function Uc(b,a,c){var d=[];q(b,function(b,g,f){d.push(a.call(c,b,g,f))});return d}function db(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Oa(b,a){var c=db(b,a);0<=c&&b.splice(c,1);return a}function ba(b,a){if(Ca(b)||b&&b.$evalAsync&&b.$watch)throw Pa("cpws");
|
||||
if(a){if(b===a)throw Pa("cpi");if(M(b))for(var c=a.length=0;c<b.length;c++)a.push(ba(b[c]));else{c=a.$$hashKey;q(a,function(b,c){delete a[c]});for(var d in b)a[d]=ba(b[d]);Sb(a,c)}}else(a=b)&&(M(b)?a=ba(b,[]):Na(b)?a=new Date(b.getTime()):cb(b)?a=RegExp(b.source):X(b)&&(a=ba(b,{})));return a}function Ub(b,a){a=a||{};for(var c in b)!b.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(a[c]=b[c]);return a}function xa(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;
|
||||
var c=typeof b,d;if(c==typeof a&&"object"==c)if(M(b)){if(!M(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!xa(b[d],a[d]))return!1;return!0}}else{if(Na(b))return Na(a)&&b.getTime()==a.getTime();if(cb(b)&&cb(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||Ca(b)||Ca(a)||M(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!P(b[d])){if(!xa(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!P(a[d]))return!1;
|
||||
return!0}return!1}function Vb(){return U.securityPolicy&&U.securityPolicy.isActive||U.querySelector&&!(!U.querySelector("[ng-csp]")&&!U.querySelector("[data-ng-csp]"))}function eb(b,a){var c=2<arguments.length?ya.call(arguments,2):[];return!P(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(ya.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Vc(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=
|
||||
s:Ca(a)?c="$WINDOW":a&&U===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function qa(b,a){return"undefined"===typeof b?s:JSON.stringify(b,Vc,a?" ":null)}function Wb(b){return w(b)?JSON.parse(b):b}function Qa(b){"function"===typeof b?b=!0:b&&0!==b.length?(b=K(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function ha(b){b=y(b).clone();try{b.empty()}catch(a){}var c=y("<div>").append(b).html();try{return 3===b[0].nodeType?K(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,
|
||||
function(a,b){return"<"+K(b)})}catch(d){return K(c)}}function Xb(b){try{return decodeURIComponent(b)}catch(a){}}function Yb(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=Xb(c[0]),B(d)&&(b=B(c[1])?Xb(c[1]):!0,a[d]?M(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Zb(b){var a=[];q(b,function(b,d){M(b)?q(b,function(b){a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))}):a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))});return a.length?a.join("&"):""}function wb(b){return za(b,
|
||||
!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function za(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Wc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,f=["ng:app","ng-app","x-ng-app","data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(f,function(a){f[a]=!0;c(U.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+
|
||||
a+"\\:"),c),q(b.querySelectorAll("["+a+"]"),c))});q(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&f[b.name]&&(e=a,g=b.value)})}});e&&a(e,g?[g]:[])}function $b(b,a){var c=function(){b=y(b);if(b.injector()){var c=b[0]===U?"document":ha(b);throw Pa("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=ac(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",
|
||||
function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(O&&!d.test(O.name))return c();O.name=O.name.replace(d,"");Ea.resumeBootstrap=function(b){q(b,function(b){a.push(b)});c()}}function fb(b,a){a=a||"_";return b.replace(Xc,function(b,d){return(d?a:"")+b.toLowerCase()})}function xb(b,a,c){if(!b)throw Pa("areq",a||"?",c||"required");return b}function Ra(b,a,c){c&&M(b)&&(b=b[b.length-1]);xb(P(b),a,"not a function, got "+(b&&"object"==typeof b?
|
||||
b.constructor.name||"Object":typeof b));return b}function Aa(b,a){if("hasOwnProperty"===b)throw Pa("badname",a);}function bc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,g=a.length,f=0;f<g;f++)d=a[f],b&&(b=(e=b)[d]);return!c&&P(b)?eb(e,b):b}function yb(b){var a=b[0];b=b[b.length-1];if(a===b)return y(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return y(c)}function Yc(b){var a=t("$injector"),c=t("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||t;return b.module||
|
||||
(b.module=function(){var b={};return function(e,g,f){if("hasOwnProperty"===e)throw c("badname","module");g&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);return n}}if(!g)throw a("nomod",e);var c=[],d=[],m=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:g,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide",
|
||||
"constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:m,run:function(a){d.push(a);return this}};f&&m(f);return n}())}}())}function Zc(b){D(b,{bootstrap:$b,copy:ba,extend:D,equals:xa,element:y,forEach:q,injector:ac,noop:C,bind:eb,toJson:qa,fromJson:Wb,identity:Da,isUndefined:E,isDefined:B,isString:w,isFunction:P,isObject:X,isNumber:vb,isElement:Tc,isArray:M,
|
||||
version:$c,isDate:Na,lowercase:K,uppercase:Fa,callbacks:{counter:0},$$minErr:t,$$csp:Vb});Sa=Yc(O);try{Sa("ngLocale")}catch(a){Sa("ngLocale",[]).provider("$locale",ad)}Sa("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:bd});a.provider("$compile",cc).directive({a:cd,input:dc,textarea:dc,form:dd,script:ed,select:fd,style:gd,option:hd,ngBind:id,ngBindHtml:jd,ngBindTemplate:kd,ngClass:ld,ngClassEven:md,ngClassOdd:nd,ngCloak:od,ngController:pd,ngForm:qd,ngHide:rd,ngIf:sd,ngInclude:td,
|
||||
ngInit:ud,ngNonBindable:vd,ngPluralize:wd,ngRepeat:xd,ngShow:yd,ngStyle:zd,ngSwitch:Ad,ngSwitchWhen:Bd,ngSwitchDefault:Cd,ngOptions:Dd,ngTransclude:Ed,ngModel:Fd,ngList:Gd,ngChange:Hd,required:ec,ngRequired:ec,ngValue:Id}).directive({ngInclude:Jd}).directive(zb).directive(fc);a.provider({$anchorScroll:Kd,$animate:Ld,$browser:Md,$cacheFactory:Nd,$controller:Od,$document:Pd,$exceptionHandler:Qd,$filter:gc,$interpolate:Rd,$interval:Sd,$http:Td,$httpBackend:Ud,$location:Vd,$log:Wd,$parse:Xd,$rootScope:Yd,
|
||||
$q:Zd,$sce:$d,$sceDelegate:ae,$sniffer:be,$templateCache:ce,$timeout:de,$window:ee,$$rAF:fe,$$asyncCallback:ge})}])}function Ta(b){return b.replace(he,function(a,b,d,e){return e?d.toUpperCase():d}).replace(ie,"Moz$1")}function Ab(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:[this],l=a,k,m,n,p,r,z;if(!d||null!=b)for(;e.length;)for(k=e.shift(),m=0,n=k.length;m<n;m++)for(p=y(k[m]),l?p.triggerHandler("$destroy"):l=!l,r=0,p=(z=p.children()).length;r<p;r++)e.push(Ga(z[r]));return g.apply(this,arguments)}
|
||||
var g=Ga.fn[b],g=g.$original||g;e.$original=g;Ga.fn[b]=e}function N(b){if(b instanceof N)return b;w(b)&&(b=ca(b));if(!(this instanceof N)){if(w(b)&&"<"!=b.charAt(0))throw Bb("nosel");return new N(b)}if(w(b)){var a=b;b=U;var c;if(c=je.exec(a))b=[b.createElement(c[1])];else{var d=b,e;b=d.createDocumentFragment();c=[];if(Cb.test(a)){d=b.appendChild(d.createElement("div"));e=(ke.exec(a)||["",""])[1].toLowerCase();e=ea[e]||ea._default;d.innerHTML="<div> </div>"+e[1]+a.replace(le,"<$1></$2>")+e[2];
|
||||
d.removeChild(d.firstChild);for(a=e[0];a--;)d=d.lastChild;a=0;for(e=d.childNodes.length;a<e;++a)c.push(d.childNodes[a]);d=b.firstChild;d.textContent=""}else c.push(d.createTextNode(a));b.textContent="";b.innerHTML="";b=c}Db(this,b);y(U.createDocumentFragment()).append(this)}else Db(this,b)}function Eb(b){return b.cloneNode(!0)}function Ha(b){hc(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ha(b[a])}function ic(b,a,c,d){if(B(d))throw Bb("offargs");var e=la(b,"events");la(b,"handle")&&(E(a)?q(e,
|
||||
function(a,c){Fb(b,c,a);delete e[c]}):q(a.split(" "),function(a){E(c)?(Fb(b,a,e[a]),delete e[a]):Oa(e[a]||[],c)}))}function hc(b,a){var c=b[gb],d=Ua[c];d&&(a?delete Ua[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),ic(b)),delete Ua[c],b[gb]=s))}function la(b,a,c){var d=b[gb],d=Ua[d||-1];if(B(c))d||(b[gb]=d=++me,d=Ua[d]={}),d[a]=c;else return d&&d[a]}function jc(b,a,c){var d=la(b,"data"),e=B(c),g=!e&&B(a),f=g&&!X(a);d||f||la(b,"data",d={});if(e)d[a]=c;else if(g){if(f)return d&&d[a];
|
||||
D(d,a)}else return d}function Gb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function hb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",ca((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+ca(a)+" "," ")))})}function ib(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(a.split(" "),function(a){a=ca(a);-1===c.indexOf(" "+a+" ")&&
|
||||
(c+=a+" ")});b.setAttribute("class",ca(c))}}function Db(b,a){if(a){a=a.nodeName||!B(a.length)||Ca(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function kc(b,a){return jb(b,"$"+(a||"ngController")+"Controller")}function jb(b,a,c){b=y(b);9==b[0].nodeType&&(b=b.find("html"));for(a=M(a)?a:[a];b.length;){for(var d=b[0],e=0,g=a.length;e<g;e++)if((c=b.data(a[e]))!==s)return c;b=y(d.parentNode||11===d.nodeType&&d.host)}}function lc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ha(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}
|
||||
function mc(b,a){var c=kb[a.toLowerCase()];return c&&nc[b.nodeName]&&c}function ne(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||U);if(E(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var f=Ub(a[e||
|
||||
c.type]||[]);q(f,function(a){a.call(b,c)});8>=S?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ia(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=b.$$hashKey():c===s&&(c=b.$$hashKey=bb()):c=b;return a+":"+c}function Va(b){q(b,this.put,this)}function oc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(oe,
|
||||
""),c=c.match(pe),q(c[1].split(qe),function(b){b.replace(re,function(b,c,d){a.push(d)})})),b.$inject=a):M(b)?(c=b.length-1,Ra(b[c],"fn"),a=b.slice(0,c)):Ra(b,"fn",!0);return a}function ac(b){function a(a){return function(b,c){if(X(b))q(b,Rb(a));else return a(b,c)}}function c(a,b){Aa(a,"service");if(P(b)||M(b))b=n.instantiate(b);if(!b.$get)throw Wa("pget",a);return m[a+h]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,g,h;q(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(w(a))for(c=
|
||||
Sa(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,g=0,h=d.length;g<h;g++){var f=d[g],l=n.get(f[0]);l[f[1]].apply(l,f[2])}else P(a)?b.push(n.invoke(a)):M(a)?b.push(n.invoke(a)):Ra(a,"module")}catch(m){throw M(a)&&(a=a[a.length-1]),m.message&&(m.stack&&-1==m.stack.indexOf(m.message))&&(m=m.message+"\n"+m.stack),Wa("modulerr",a,m.stack||m.message||m);}}});return b}function g(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===f)throw Wa("cdep",l.join(" <- "));return a[d]}try{return l.unshift(d),
|
||||
a[d]=f,a[d]=b(d)}catch(e){throw a[d]===f&&delete a[d],e;}finally{l.shift()}}function d(a,b,e){var g=[],h=oc(a),f,l,k;l=0;for(f=h.length;l<f;l++){k=h[l];if("string"!==typeof k)throw Wa("itkn",k);g.push(e&&e.hasOwnProperty(k)?e[k]:c(k))}a.$inject||(a=a[f]);return a.apply(b,g)}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(M(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return X(e)||P(e)?e:c},get:c,annotate:oc,has:function(b){return m.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}
|
||||
var f={},h="Provider",l=[],k=new Va,m={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,aa(b))}),constant:a(function(a,b){Aa(a,"constant");m[a]=b;p[a]=b}),decorator:function(a,b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=r.invoke(d,c);return r.invoke(b,null,{$delegate:a})}}}},n=m.$injector=g(m,function(){throw Wa("unpr",l.join(" <- "));}),p={},r=p.$injector=g(p,function(a){a=n.get(a+
|
||||
h);return r.invoke(a.$get,a)});q(e(b),function(a){r.invoke(a||C)});return r}function Kd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==K(a.nodeName)||(b=a)});return b}function g(){var b=c.hash(),d;b?(d=f.getElementById(b))?d.scrollIntoView():(d=e(f.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var f=a.document;b&&d.$watch(function(){return c.hash()},
|
||||
function(){d.$evalAsync(g)});return g}]}function ge(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function se(b,a,c,d){function e(a){try{a.apply(null,ya.call(arguments,1))}finally{if(z--,0===z)for(;u.length;)try{u.pop()()}catch(b){c.error(b)}}}function g(a,b){(function T(){q(F,function(a){a()});v=b(T,a)})()}function f(){x=null;J!=h.url()&&(J=h.url(),q(ma,function(a){a(h.url())}))}var h=this,l=a[0],k=b.location,m=b.history,
|
||||
n=b.setTimeout,p=b.clearTimeout,r={};h.isMock=!1;var z=0,u=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){z++};h.notifyWhenNoOutstandingRequests=function(a){q(F,function(a){a()});0===z?a():u.push(a)};var F=[],v;h.addPollFn=function(a){E(v)&&g(100,n);F.push(a);return a};var J=k.href,A=a.find("base"),x=null;h.url=function(a,c){k!==b.location&&(k=b.location);m!==b.history&&(m=b.history);if(a){if(J!=a)return J=a,d.history?c?m.replaceState(null,"",a):(m.pushState(null,"",
|
||||
a),A.attr("href",A.attr("href"))):(x=a,c?k.replace(a):k.href=a),h}else return x||k.href.replace(/%27/g,"'")};var ma=[],L=!1;h.onUrlChange=function(a){if(!L){if(d.history)y(b).on("popstate",f);if(d.hashchange)y(b).on("hashchange",f);else h.addPollFn(f);L=!0}ma.push(a);return a};h.baseHref=function(){var a=A.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var Q={},da="",H=h.baseHref();h.cookies=function(a,b){var d,e,g,h;if(a)b===s?l.cookie=escape(a)+"=;path="+H+";expires=Thu, 01 Jan 1970 00:00:00 GMT":
|
||||
w(b)&&(d=(l.cookie=escape(a)+"="+escape(b)+";path="+H).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(l.cookie!==da)for(da=l.cookie,d=da.split("; "),Q={},g=0;g<d.length;g++)e=d[g],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,h)),Q[a]===s&&(Q[a]=unescape(e.substring(h+1))));return Q}};h.defer=function(a,b){var c;z++;c=n(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};h.defer.cancel=function(a){return r[a]?(delete r[a],
|
||||
p(a),e(C),!0):!1}}function Md(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new se(b,d,a,c)}]}function Nd(){this.$get=function(){function b(b,d){function e(a){a!=n&&(p?p==a&&(p=a.n):p=a,g(a.n,a.p),g(a,n),n=a,n.n=null)}function g(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw t("$cacheFactory")("iid",b);var f=0,h=D({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,m={},n=null,p=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=m[a]||(m[a]={key:a});
|
||||
e(c)}if(!E(b))return a in l||f++,l[a]=b,f>k&&this.remove(p.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=m[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=m[a];if(!b)return;b==n&&(n=b.p);b==p&&(p=b.n);g(b.n,b.p);delete m[a]}delete l[a];f--},removeAll:function(){l={};f=0;m={};n=p=null},destroy:function(){m=h=l=null;delete a[b]},info:function(){return D({},h,{size:f})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};
|
||||
return b}}function ce(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function cc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,g=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,f=/^(on[a-z]+|formaction)$/;this.directive=function l(a,e){Aa(a,"directive");w(a)?(xb(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];q(c[a],function(c,g){try{var f=b.invoke(c);P(f)?f={compile:aa(f)}:!f.compile&&f.link&&(f.compile=
|
||||
aa(f.link));f.priority=f.priority||0;f.index=g;f.name=f.name||a;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(l){d(l)}});return e}])),c[a].push(e)):q(a,Rb(l));return this};this.aHrefSanitizationWhitelist=function(b){return B(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate",
|
||||
"$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,m,n,p,r,z,u,F,v,J,A){function x(a,b,c,d,e){a instanceof y||(a=y(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});var g=L(a,b,a,c,d,e);ma(a,"ng-scope");return function(b,c,d){xb(b,"scope");var e=c?Ja.clone.call(a):a;q(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var f=e.length;d<f;d++){var l=
|
||||
e[d].nodeType;1!==l&&9!==l||e.eq(d).data("$scope",b)}c&&c(e,b);g&&g(b,e,e);return e}}function ma(a,b){try{a.addClass(b)}catch(c){}}function L(a,b,c,d,e,g){function f(a,c,d,e){var g,k,m,r,n,p,z;g=c.length;var I=Array(g);for(n=0;n<g;n++)I[n]=c[n];z=n=0;for(p=l.length;n<p;z++)k=I[z],c=l[n++],g=l[n++],m=y(k),c?(c.scope?(r=a.$new(),m.data("$scope",r)):r=a,(m=c.transclude)||!e&&b?c(g,r,k,d,Q(a,m||b)):c(g,r,k,d,e)):g&&g(a,k.childNodes,s,e)}for(var l=[],k,m,r,n,p=0;p<a.length;p++)k=new Hb,m=da(a[p],[],k,
|
||||
0===p?d:s,e),(g=m.length?ia(m,a[p],k,b,c,null,[],[],g):null)&&g.scope&&ma(y(a[p]),"ng-scope"),k=g&&g.terminal||!(r=a[p].childNodes)||!r.length?null:L(r,g?g.transclude:b),l.push(g,k),n=n||g||k,g=null;return n?f:null}function Q(a,b){return function(c,d,e){var g=!1;c||(c=a.$new(),g=c.$$transcluded=!0);d=b(c,d,e);if(g)d.on("$destroy",eb(c,c.$destroy));return d}}function da(a,b,c,d,f){var k=c.$attr,l;switch(a.nodeType){case 1:T(b,na(Ka(a).toLowerCase()),"E",d,f);var m,r,n;l=a.attributes;for(var p=0,z=
|
||||
l&&l.length;p<z;p++){var u=!1,F=!1;m=l[p];if(!S||8<=S||m.specified){r=m.name;n=na(r);W.test(n)&&(r=fb(n.substr(6),"-"));var J=n.replace(/(Start|End)$/,"");n===J+"Start"&&(u=r,F=r.substr(0,r.length-5)+"end",r=r.substr(0,r.length-6));n=na(r.toLowerCase());k[n]=r;c[n]=m=ca(m.value);mc(a,n)&&(c[n]=!0);N(a,b,m,n);T(b,n,"A",d,f,u,F)}}a=a.className;if(w(a)&&""!==a)for(;l=g.exec(a);)n=na(l[2]),T(b,n,"C",d,f)&&(c[n]=ca(l[3])),a=a.substr(l.index+l[0].length);break;case 3:t(b,a.nodeValue);break;case 8:try{if(l=
|
||||
e.exec(a.nodeValue))n=na(l[1]),T(b,n,"M",d,f)&&(c[n]=ca(l[2]))}catch(x){}}b.sort(E);return b}function H(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ja("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return y(d)}function R(a,b,c){return function(d,e,g,f,l){e=H(e[0],b,c);return a(d,e,g,f,l)}}function ia(a,c,d,e,g,f,l,n,p){function u(a,b,c,d){if(a){c&&(a=R(a,c,d));a.require=G.require;if(Q===
|
||||
G||G.$$isolateScope)a=qc(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=R(b,c,d));b.require=G.require;if(Q===G||G.$$isolateScope)b=qc(b,{isolateScope:!0});n.push(b)}}function F(a,b,c){var d,e="data",g=!1;if(w(a)){for(;"^"==(d=a.charAt(0))||"?"==d;)a=a.substr(1),"^"==d&&(e="inheritedData"),g=g||"?"==d;d=null;c&&"data"===e&&(d=c[a]);d=d||b[e]("$"+a+"Controller");if(!d&&!g)throw ja("ctreq",a,t);}else M(a)&&(d=[],q(a,function(a){d.push(F(a,b,c))}));return d}function J(a,e,g,f,p){function u(a,b){var c;2>arguments.length&&
|
||||
(b=a,a=s);D&&(c=lb);return p(a,b,c)}var I,x,v,A,R,H,lb={},da;I=c===g?d:Ub(d,new Hb(y(g),d.$attr));x=I.$$element;if(Q){var T=/^\s*([@=&])(\??)\s*(\w*)\s*$/;f=y(g);H=e.$new(!0);ia&&ia===Q.$$originalDirective?f.data("$isolateScope",H):f.data("$isolateScopeNoTemplate",H);ma(f,"ng-isolate-scope");q(Q.scope,function(a,c){var d=a.match(T)||[],g=d[3]||c,f="?"==d[2],d=d[1],l,m,n,p;H.$$isolateBindings[c]=d+g;switch(d){case "@":I.$observe(g,function(a){H[c]=a});I.$$observers[g].$$scope=e;I[g]&&(H[c]=b(I[g])(e));
|
||||
break;case "=":if(f&&!I[g])break;m=r(I[g]);p=m.literal?xa:function(a,b){return a===b};n=m.assign||function(){l=H[c]=m(e);throw ja("nonassign",I[g],Q.name);};l=H[c]=m(e);H.$watch(function(){var a=m(e);p(a,H[c])||(p(a,l)?n(e,a=H[c]):H[c]=a);return l=a},null,m.literal);break;case "&":m=r(I[g]);H[c]=function(a){return m(e,a)};break;default:throw ja("iscp",Q.name,c,a);}})}da=p&&u;L&&q(L,function(a){var b={$scope:a===Q||a.$$isolateScope?H:e,$element:x,$attrs:I,$transclude:da},c;R=a.controller;"@"==R&&(R=
|
||||
I[a.name]);c=z(R,b);lb[a.name]=c;D||x.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});f=0;for(v=l.length;f<v;f++)try{A=l[f],A(A.isolateScope?H:e,x,I,A.require&&F(A.require,x,lb),da)}catch(G){m(G,ha(x))}f=e;Q&&(Q.template||null===Q.templateUrl)&&(f=H);a&&a(f,g.childNodes,s,p);for(f=n.length-1;0<=f;f--)try{A=n[f],A(A.isolateScope?H:e,x,I,A.require&&F(A.require,x,lb),da)}catch(B){m(B,ha(x))}}p=p||{};for(var v=-Number.MAX_VALUE,A,L=p.controllerDirectives,Q=p.newIsolateScopeDirective,
|
||||
ia=p.templateDirective,T=p.nonTlbTranscludeDirective,E=!1,D=p.hasElementTranscludeDirective,Z=d.$$element=y(c),G,t,V,Xa=e,O,N=0,S=a.length;N<S;N++){G=a[N];var ra=G.$$start,W=G.$$end;ra&&(Z=H(c,ra,W));V=s;if(v>G.priority)break;if(V=G.scope)A=A||G,G.templateUrl||(K("new/isolated scope",Q,G,Z),X(V)&&(Q=G));t=G.name;!G.templateUrl&&G.controller&&(V=G.controller,L=L||{},K("'"+t+"' controller",L[t],G,Z),L[t]=G);if(V=G.transclude)E=!0,G.$$tlb||(K("transclusion",T,G,Z),T=G),"element"==V?(D=!0,v=G.priority,
|
||||
V=H(c,ra,W),Z=d.$$element=y(U.createComment(" "+t+": "+d[t]+" ")),c=Z[0],mb(g,y(ya.call(V,0)),c),Xa=x(V,e,v,f&&f.name,{nonTlbTranscludeDirective:T})):(V=y(Eb(c)).contents(),Z.empty(),Xa=x(V,e));if(G.template)if(K("template",ia,G,Z),ia=G,V=P(G.template)?G.template(Z,d):G.template,V=Y(V),G.replace){f=G;V=Cb.test(V)?y(V):[];c=V[0];if(1!=V.length||1!==c.nodeType)throw ja("tplrt",t,"");mb(g,Z,c);S={$attr:{}};V=da(c,[],S);var $=a.splice(N+1,a.length-(N+1));Q&&pc(V);a=a.concat(V).concat($);B(d,S);S=a.length}else Z.html(V);
|
||||
if(G.templateUrl)K("template",ia,G,Z),ia=G,G.replace&&(f=G),J=C(a.splice(N,a.length-N),Z,d,g,Xa,l,n,{controllerDirectives:L,newIsolateScopeDirective:Q,templateDirective:ia,nonTlbTranscludeDirective:T}),S=a.length;else if(G.compile)try{O=G.compile(Z,d,Xa),P(O)?u(null,O,ra,W):O&&u(O.pre,O.post,ra,W)}catch(aa){m(aa,ha(Z))}G.terminal&&(J.terminal=!0,v=Math.max(v,G.priority))}J.scope=A&&!0===A.scope;J.transclude=E&&Xa;p.hasElementTranscludeDirective=D;return J}function pc(a){for(var b=0,c=a.length;b<c;b++)a[b]=
|
||||
Tb(a[b],{$$isolateScope:!0})}function T(b,e,g,f,k,n,r){if(e===k)return null;k=null;if(c.hasOwnProperty(e)){var p;e=a.get(e+d);for(var z=0,u=e.length;z<u;z++)try{p=e[z],(f===s||f>p.priority)&&-1!=p.restrict.indexOf(g)&&(n&&(p=Tb(p,{$$start:n,$$end:r})),b.push(p),k=p)}catch(F){m(F)}}return k}function B(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,g){"class"==g?(ma(e,b),a["class"]=(a["class"]?
|
||||
a["class"]+" ":"")+b):"style"==g?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==g.charAt(0)||a.hasOwnProperty(g)||(a[g]=b,d[g]=c[g])})}function C(a,b,c,d,e,g,f,l){var k=[],m,r,z=b[0],u=a.shift(),F=D({},u,{templateUrl:null,transclude:null,replace:null,$$originalDirective:u}),x=P(u.templateUrl)?u.templateUrl(b,c):u.templateUrl;b.empty();n.get(v.getTrustedResourceUrl(x),{cache:p}).success(function(n){var p,J;n=Y(n);if(u.replace){n=Cb.test(n)?y(n):[];p=n[0];if(1!=n.length||
|
||||
1!==p.nodeType)throw ja("tplrt",u.name,x);n={$attr:{}};mb(d,b,p);var v=da(p,[],n);X(u.scope)&&pc(v);a=v.concat(a);B(c,n)}else p=z,b.html(n);a.unshift(F);m=ia(a,p,c,e,b,u,g,f,l);q(d,function(a,c){a==p&&(d[c]=b[0])});for(r=L(b[0].childNodes,e);k.length;){n=k.shift();J=k.shift();var A=k.shift(),R=k.shift(),v=b[0];if(J!==z){var H=J.className;l.hasElementTranscludeDirective&&u.replace||(v=Eb(p));mb(A,y(J),v);ma(y(v),H)}J=m.transclude?Q(n,m.transclude):R;m(r,n,v,d,J)}k=null}).error(function(a,b,c,d){throw ja("tpload",
|
||||
d.url);});return function(a,b,c,d,e){k?(k.push(b),k.push(c),k.push(d),k.push(e)):m(r,b,c,d,e)}}function E(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function K(a,b,c,d){if(b)throw ja("multidir",b.name,c.name,a,ha(d));}function t(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:aa(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);ma(c.data("$binding",e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function O(a,b){if("srcdoc"==
|
||||
b)return v.HTML;var c=Ka(a);if("xlinkHref"==b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return v.RESOURCE_URL}function N(a,c,d,e){var g=b(d,!0);if(g){if("multiple"===e&&"SELECT"===Ka(a))throw ja("selmulti",ha(a));c.push({priority:100,compile:function(){return{pre:function(c,d,l){d=l.$$observers||(l.$$observers={});if(f.test(e))throw ja("nodomevents");if(g=b(l[e],!0,O(a,e)))l[e]=g(c),(d[e]||(d[e]=[])).$$inter=!0,(l.$$observers&&l.$$observers[e].$$scope||c).$watch(g,function(a,b){"class"===
|
||||
e&&a!=b?l.$updateClass(a,b):l.$set(e,a)})}}}})}}function mb(a,b,c){var d=b[0],e=b.length,g=d.parentNode,f,l;if(a)for(f=0,l=a.length;f<l;f++)if(a[f]==d){a[f++]=c;l=f+e-1;for(var k=a.length;f<k;f++,l++)l<k?a[f]=a[l]:delete a[f];a.length-=e-1;break}g&&g.replaceChild(c,d);a=U.createDocumentFragment();a.appendChild(d);c[y.expando]=d[y.expando];d=1;for(e=b.length;d<e;d++)g=b[d],y(g).remove(),a.appendChild(g),delete b[d];b[0]=c;b.length=1}function qc(a,b){return D(function(){return a.apply(null,arguments)},
|
||||
a,b)}var Hb=function(a,b){this.$$element=a;this.$attr=b||{}};Hb.prototype={$normalize:na,$addClass:function(a){a&&0<a.length&&J.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&J.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=rc(a,b),d=rc(b,a);0===c.length?J.removeClass(this.$$element,d):0===d.length?J.addClass(this.$$element,c):J.setClass(this.$$element,c,d)},$set:function(a,b,c,d){var e=mc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=
|
||||
d:(d=this.$attr[a])||(this.$attr[a]=d=fb(a,"-"));e=Ka(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=A(b,"src"===a);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){m(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);u.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var Z=b.startSymbol(),ra=b.endSymbol(),Y="{{"==Z||"}}"==ra?
|
||||
Da:function(a){return a.replace(/\{\{/g,Z).replace(/}}/g,ra)},W=/^ngAttr[A-Z]/;return x}]}function na(b){return Ta(b.replace(te,""))}function rc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),g=0;a:for(;g<d.length;g++){for(var f=d[g],h=0;h<e.length;h++)if(f==e[h])continue a;c+=(0<c.length?" ":"")+f}return c}function Od(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){Aa(a,"controller");X(a)?D(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,g){var f,
|
||||
h,l;w(e)&&(f=e.match(a),h=f[1],l=f[3],e=b.hasOwnProperty(h)?b[h]:bc(g.$scope,h,!0)||bc(d,h,!0),Ra(e,h,!0));f=c.instantiate(e,g);if(l){if(!g||"object"!=typeof g.$scope)throw t("$controller")("noscp",h||e.name,l);g.$scope[l]=f}return f}}]}function Pd(){this.$get=["$window",function(b){return y(b.document)}]}function Qd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function sc(b){var a={},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=K(ca(b.substr(0,
|
||||
e)));d=ca(b.substr(e+1));c&&(a[c]=a[c]?a[c]+(", "+d):d)});return a}function tc(b){var a=X(b)?b:s;return function(c){a||(a=sc(b));return c?a[K(c)]||null:a}}function uc(b,a,c){if(P(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function Td(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){w(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=Wb(d)));return d}],transformRequest:[function(a){return X(a)&&
|
||||
"[object File]"!==wa.call(a)&&"[object Blob]"!==wa.call(a)?qa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ba(d),put:ba(d),patch:ba(d)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},g=this.interceptors=[],f=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,p){function r(a){function c(a){var b=D({},a,{data:uc(a.data,a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?
|
||||
b:n.reject(b)}var d={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},g=function(a){function b(a){var c;q(a,function(b,d){P(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=D({},a.headers),g,f,c=D({},c.common,c[K(a.method)]);b(c);b(d);a:for(g in c){a=K(g);for(f in d)if(K(f)===a)continue a;d[g]=c[g]}return d}(a);D(d,a);d.headers=g;d.method=Fa(d.method);(a=Ib(d.url)?b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:s)&&(g[d.xsrfHeaderName||e.xsrfHeaderName]=
|
||||
a);var f=[function(a){g=a.headers;var b=uc(a.data,tc(g),a.transformRequest);E(a.data)&&q(g,function(a,b){"content-type"===K(b)&&delete g[b]});E(a.withCredentials)&&!E(e.withCredentials)&&(a.withCredentials=e.withCredentials);return z(a,b,g).then(c,c)},s],h=n.when(d);for(q(v,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),h=h.then(a,k)}h.success=function(a){h.then(function(b){a(b.data,
|
||||
b.status,b.headers,d)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,d)});return h};return h}function z(b,c,g){function f(a,b,c,e){v&&(200<=a&&300>a?v.put(s,[a,b,sc(c),e]):v.remove(s));l(b,a,c,e);d.$$phase||d.$apply()}function l(a,c,d,e){c=Math.max(c,0);(200<=c&&300>c?p.resolve:p.reject)({data:a,status:c,headers:tc(d),config:b,statusText:e})}function k(){var a=db(r.pendingRequests,b);-1!==a&&r.pendingRequests.splice(a,1)}var p=n.defer(),z=p.promise,v,q,s=u(b.url,
|
||||
b.params);r.pendingRequests.push(b);z.then(k,k);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(v=X(b.cache)?b.cache:X(e.cache)?e.cache:F);if(v)if(q=v.get(s),B(q)){if(q.then)return q.then(k,k),q;M(q)?l(q[1],q[0],ba(q[2]),q[3]):l(q,200,{},"OK")}else v.put(s,z);E(q)&&a(b.method,s,c,f,g,b.timeout,b.withCredentials,b.responseType);return z}function u(a,b){if(!b)return a;var c=[];Sc(b,function(a,b){null===a||E(a)||(M(a)||(a=[a]),q(a,function(a){X(a)&&(a=qa(a));c.push(za(b)+"="+za(a))}))});0<c.length&&
|
||||
(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var F=c("$http"),v=[];q(g,function(a){v.unshift(w(a)?p.get(a):p.invoke(a))});q(f,function(a,b){var c=w(a)?p.get(a):p.invoke(a);v.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});r.pendingRequests=[];(function(a){q(arguments,function(a){r[a]=function(b,c){return r(D(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){r[a]=function(b,c,d){return r(D(d||
|
||||
{},{method:a,url:b,data:c}))}})})("post","put");r.defaults=e;return r}]}function ue(b){if(8>=S&&(!b.match(/^(get|post|head|put|delete|options)$/i)||!O.XMLHttpRequest))return new O.ActiveXObject("Microsoft.XMLHTTP");if(O.XMLHttpRequest)return new O.XMLHttpRequest;throw t("$httpBackend")("noxhr");}function Ud(){this.$get=["$browser","$window","$document",function(b,a,c){return ve(b,ue,b.defer,a.angular.callbacks,c[0])}]}function ve(b,a,c,d,e){function g(a,b){var c=e.createElement("script"),d=function(){c.onreadystatechange=
|
||||
c.onload=c.onerror=null;e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;S&&8>=S?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror=function(){d()};e.body.appendChild(c);return d}var f=-1;return function(e,l,k,m,n,p,r,z){function u(){v=f;A&&A();x&&x.abort()}function F(a,d,e,g,f){L&&c.cancel(L);A=x=null;0===d&&(d=e?200:"file"==sa(l).protocol?404:0);a(1223===d?204:d,e,g,f||"");b.$$completeOutstandingRequest(C)}var v;b.$$incOutstandingRequestCount();
|
||||
l=l||b.url();if("jsonp"==K(e)){var J="_"+(d.counter++).toString(36);d[J]=function(a){d[J].data=a};var A=g(l.replace("JSON_CALLBACK","angular.callbacks."+J),function(){d[J].data?F(m,200,d[J].data):F(m,v||-2);d[J]=Ea.noop})}else{var x=a(e);x.open(e,l,!0);q(n,function(a,b){B(a)&&x.setRequestHeader(b,a)});x.onreadystatechange=function(){if(x&&4==x.readyState){var a=null,b=null;v!==f&&(a=x.getAllResponseHeaders(),b="response"in x?x.response:x.responseText);F(m,v||x.status,b,a,x.statusText||"")}};r&&(x.withCredentials=
|
||||
!0);if(z)try{x.responseType=z}catch(s){if("json"!==z)throw s;}x.send(k||null)}if(0<p)var L=c(u,p);else p&&p.then&&p.then(u)}}function Rd(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function g(g,k,m){for(var n,p,r=0,z=[],u=g.length,F=!1,v=[];r<u;)-1!=(n=g.indexOf(b,r))&&-1!=(p=g.indexOf(a,n+f))?(r!=n&&z.push(g.substring(r,n)),z.push(r=c(F=g.substring(n+f,p))),
|
||||
r.exp=F,r=p+h,F=!0):(r!=u&&z.push(g.substring(r)),r=u);(u=z.length)||(z.push(""),u=1);if(m&&1<z.length)throw vc("noconcat",g);if(!k||F)return v.length=u,r=function(a){try{for(var b=0,c=u,f;b<c;b++)"function"==typeof(f=z[b])&&(f=f(a),f=m?e.getTrusted(m,f):e.valueOf(f),null===f||E(f)?f="":"string"!=typeof f&&(f=qa(f))),v[b]=f;return v.join("")}catch(h){a=vc("interr",g,h.toString()),d(a)}},r.exp=g,r.parts=z,r}var f=b.length,h=a.length;g.startSymbol=function(){return b};g.endSymbol=function(){return a};
|
||||
return g}]}function Sd(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,f,h,l){var k=a.setInterval,m=a.clearInterval,n=c.defer(),p=n.promise,r=0,z=B(l)&&!l;h=B(h)?h:0;p.then(null,null,d);p.$$intervalId=k(function(){n.notify(r++);0<h&&r>=h&&(n.resolve(r),m(p.$$intervalId),delete e[p.$$intervalId]);z||b.$apply()},f);e[p.$$intervalId]=n;return p}var e={};d.cancel=function(a){return a&&a.$$intervalId in e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],
|
||||
!0):!1};return d}]}function ad(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
|
||||
DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function wc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=wb(b[a]);return b.join("/")}function xc(b,a,c){b=sa(b,c);a.$$protocol=
|
||||
b.protocol;a.$$host=b.hostname;a.$$port=Y(b.port)||we[b.protocol]||null}function yc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=sa(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=Yb(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function oa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ya(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Jb(b){return b.substr(0,
|
||||
Ya(b).lastIndexOf("/")+1)}function zc(b,a){this.$$html5=!0;a=a||"";var c=Jb(b);xc(b,this,b);this.$$parse=function(a){var e=oa(c,a);if(!w(e))throw Kb("ipthprfx",a,c);yc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Zb(this.$$search),b=this.$$hash?"#"+wb(this.$$hash):"";this.$$url=wc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=oa(b,d))!==s)return d=e,(e=oa(a,e))!==s?c+(oa("/",e)||e):b+d;if((e=oa(c,
|
||||
d))!==s)return c+e;if(c==d+"/")return c}}function Lb(b,a){var c=Jb(b);xc(b,this,b);this.$$parse=function(d){var e=oa(b,d)||oa(c,d),e="#"==e.charAt(0)?oa(a,e):this.$$html5?e:"";if(!w(e))throw Kb("ihshprfx",d,a);yc(e,this,b);d=this.$$path;var g=/^\/?.*?:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));g.exec(e)||(d=(e=g.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Zb(this.$$search),e=this.$$hash?"#"+wb(this.$$hash):"";this.$$url=wc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=
|
||||
b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Ya(b)==Ya(a))return a}}function Ac(b,a){this.$$html5=!0;Lb.apply(this,arguments);var c=Jb(b);this.$$rewrite=function(d){var e;if(b==Ya(d))return d;if(e=oa(c,d))return b+a+e;if(c===d+"/")return c}}function nb(b){return function(){return this[b]}}function Bc(b,a){return function(c){if(E(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Vd(){var b="",a=!1;this.hashPrefix=function(a){return B(a)?(b=a,this):b};this.html5Mode=
|
||||
function(b){return B(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function f(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,l=d.baseHref(),k=d.url();a?(l=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(l||"/"),e=e.history?zc:Ac):(l=Ya(k),e=Lb);h=new e(l,"#"+b);h.$$parse(h.$$rewrite(k));g.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=y(a.target);"a"!==K(b[0].nodeName);)if(b[0]===g[0]||!(b=b.parent())[0])return;
|
||||
var e=b.prop("href");X(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=sa(e.animVal).href);var f=h.$$rewrite(e);e&&(!b.attr("target")&&f&&!a.isDefaultPrevented())&&(a.preventDefault(),f!=d.url()&&(h.$$parse(f),c.$apply(),O.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=k&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):f(b)}),c.$$phase||
|
||||
c.$digest())});var m=0;c.$watch(function(){var a=d.url(),b=h.$$replace;m&&a==h.absUrl()||(m++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),f(a))}));h.$$replace=!1;return m});return h}]}function Wd(){var b=!0,a=this;this.debugEnabled=function(a){return B(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:
|
||||
a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||C;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];q(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function fa(b,a){if("constructor"===b)throw Ba("isecfld",a);return b}function Za(b,
|
||||
a){if(b){if(b.constructor===b)throw Ba("isecfn",a);if(b.document&&b.location&&b.alert&&b.setInterval)throw Ba("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw Ba("isecdom",a);}return b}function ob(b,a,c,d,e){e=e||{};a=a.split(".");for(var g,f=0;1<a.length;f++){g=fa(a.shift(),d);var h=b[g];h||(h={},b[g]=h);b=h;b.then&&e.unwrapPromises&&(ta(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}g=fa(a.shift(),d);return b[g]=c}function Cc(b,
|
||||
a,c,d,e,g,f){fa(b,g);fa(a,g);fa(c,g);fa(d,g);fa(e,g);return f.unwrapPromises?function(f,l){var k=l&&l.hasOwnProperty(b)?l:f,m;if(null==k)return k;(k=k[b])&&k.then&&(ta(g),"$$v"in k||(m=k,m.$$v=s,m.then(function(a){m.$$v=a})),k=k.$$v);if(!a)return k;if(null==k)return s;(k=k[a])&&k.then&&(ta(g),"$$v"in k||(m=k,m.$$v=s,m.then(function(a){m.$$v=a})),k=k.$$v);if(!c)return k;if(null==k)return s;(k=k[c])&&k.then&&(ta(g),"$$v"in k||(m=k,m.$$v=s,m.then(function(a){m.$$v=a})),k=k.$$v);if(!d)return k;if(null==
|
||||
k)return s;(k=k[d])&&k.then&&(ta(g),"$$v"in k||(m=k,m.$$v=s,m.then(function(a){m.$$v=a})),k=k.$$v);if(!e)return k;if(null==k)return s;(k=k[e])&&k.then&&(ta(g),"$$v"in k||(m=k,m.$$v=s,m.then(function(a){m.$$v=a})),k=k.$$v);return k}:function(g,f){var k=f&&f.hasOwnProperty(b)?f:g;if(null==k)return k;k=k[b];if(!a)return k;if(null==k)return s;k=k[a];if(!c)return k;if(null==k)return s;k=k[c];if(!d)return k;if(null==k)return s;k=k[d];return e?null==k?s:k=k[e]:k}}function xe(b,a){fa(b,a);return function(a,
|
||||
d){return null==a?s:(d&&d.hasOwnProperty(b)?d:a)[b]}}function ye(b,a,c){fa(b,c);fa(a,c);return function(c,e){if(null==c)return s;c=(e&&e.hasOwnProperty(b)?e:c)[b];return null==c?s:c[a]}}function Dc(b,a,c){if(Mb.hasOwnProperty(b))return Mb[b];var d=b.split("."),e=d.length,g;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||2!==e)if(a.csp)g=6>e?Cc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,g){var f=0,h;do h=Cc(d[f++],d[f++],d[f++],d[f++],d[f++],c,a)(b,g),g=s,b=h;while(f<e);return h};else{var f="var p;\n";
|
||||
q(d,function(b,d){fa(b,c);f+="if(s == null) return undefined;\ns="+(d?"s":'((k&&k.hasOwnProperty("'+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises?'if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});var f=f+"return s;",h=new Function("s","k","pw",f);h.toString=aa(f);g=a.unwrapPromises?function(a,b){return h(a,b,ta)}:h}else g=ye(d[0],d[1],c);else g=xe(d[0],c);"hasOwnProperty"!==
|
||||
b&&(Mb[b]=g);return g}function Xd(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=function(b){return B(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return B(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(c,d,e){a.csp=d.csp;ta=function(b){a.logPromiseWarnings&&!Ec.hasOwnProperty(b)&&(Ec[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};
|
||||
return function(d){var e;switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];e=new Nb(a);e=(new $a(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return C}}}]}function Zd(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return ze(function(a){b.$evalAsync(a)},a)}]}function ze(b,a){function c(a){return a}function d(a){return f(a)}var e=function(){var f=[],k,m;return m={resolve:function(a){if(f){var c=f;f=s;k=g(a);c.length&&b(function(){for(var a,
|
||||
b=0,d=c.length;b<d;b++)a=c[b],k.then(a[0],a[1],a[2])})}},reject:function(a){m.resolve(h(a))},notify:function(a){if(f){var c=f;f.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,g,h){var m=e(),u=function(d){try{m.resolve((P(b)?b:c)(d))}catch(e){m.reject(e),a(e)}},F=function(b){try{m.resolve((P(g)?g:d)(b))}catch(c){m.reject(c),a(c)}},v=function(b){try{m.notify((P(h)?h:c)(b))}catch(d){a(d)}};f?f.push([u,F,v]):k.then(u,F,v);return m.promise},"catch":function(a){return this.then(null,
|
||||
a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,g){var f=null;try{f=(a||c)()}catch(h){return b(h,!1)}return f&&P(f.then)?f.then(function(){return b(e,g)},function(a){return b(a,!1)}):b(e,g)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},g=function(a){return a&&P(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},f=function(a){var b=e();b.reject(a);return b.promise},h=function(c){return{then:function(g,
|
||||
f){var h=e();b(function(){try{h.resolve((P(f)?f:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:f,when:function(h,k,m,n){var p=e(),r,z=function(b){try{return(P(k)?k:c)(b)}catch(d){return a(d),f(d)}},u=function(b){try{return(P(m)?m:d)(b)}catch(c){return a(c),f(c)}},F=function(b){try{return(P(n)?n:c)(b)}catch(d){a(d)}};b(function(){g(h).then(function(a){r||(r=!0,p.resolve(g(a).then(z,u,F)))},function(a){r||(r=!0,p.resolve(u(a)))},function(a){r||p.notify(F(a))})});return p.promise},
|
||||
all:function(a){var b=e(),c=0,d=M(a)?[]:{};q(a,function(a,e){c++;g(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function fe(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,g=e?
|
||||
function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};g.supported=e;return g}]}function Yd(){var b=10,a=t("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,g,f){function h(){this.$id=bb();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;
|
||||
this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings={}}function l(b){if(p.$$phase)throw a("inprog",p.$$phase);p.$$phase=b}function k(a,b){var c=g(a);Ra(c,b);return c}function m(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function n(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=
|
||||
this.$$postDigestQueue):(a=function(){},a.prototype=this,a=new a,a.$id=bb());a["this"]=a;a.$$listeners={};a.$$listenerCount={};a.$parent=this;a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,d){var e=k(a,"watch"),g=this.$$watchers,f={fn:b,last:n,get:e,exp:a,eq:!!d};c=null;if(!P(b)){var h=k(b||C,"listener");f.fn=function(a,
|
||||
b,c){h(c)}}if("string"==typeof a&&e.constant){var l=f.fn;f.fn=function(a,b,c){l.call(this,a,b,c);Oa(g,f)}}g||(g=this.$$watchers=[]);g.unshift(f);return function(){Oa(g,f);c=null}},$watchCollection:function(a,b){var c=this,d,e,f,h=1<b.length,l=0,k=g(a),m=[],n={},p=!0,q=0;return this.$watch(function(){d=k(c);var a,b;if(X(d))if(ab(d))for(e!==m&&(e=m,q=e.length=0,l++),a=d.length,q!==a&&(l++,e.length=q=a),b=0;b<a;b++)e[b]!==e[b]&&d[b]!==d[b]||e[b]===d[b]||(l++,e[b]=d[b]);else{e!==n&&(e=n={},q=0,l++);a=
|
||||
0;for(b in d)d.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?e[b]!==d[b]&&(l++,e[b]=d[b]):(q++,e[b]=d[b],l++));if(q>a)for(b in l++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(q--,delete e[b])}else e!==d&&(e=d,l++);return l},function(){p?(p=!1,b(d,d,c)):b(d,f,c);if(h)if(X(d))if(ab(d)){f=Array(d.length);for(var a=0;a<d.length;a++)f[a]=d[a]}else for(a in f={},d)Fc.call(d,a)&&(f[a]=d[a]);else f=d})},$digest:function(){var d,g,f,h,k=this.$$asyncQueue,m=this.$$postDigestQueue,q,x,s=b,L,Q=[],y,H,R;l("$digest");
|
||||
c=null;do{x=!1;for(L=this;k.length;){try{R=k.shift(),R.scope.$eval(R.expression)}catch(B){p.$$phase=null,e(B)}c=null}a:do{if(h=L.$$watchers)for(q=h.length;q--;)try{if(d=h[q])if((g=d.get(L))!==(f=d.last)&&!(d.eq?xa(g,f):"number"==typeof g&&"number"==typeof f&&isNaN(g)&&isNaN(f)))x=!0,c=d,d.last=d.eq?ba(g):g,d.fn(g,f===n?g:f,L),5>s&&(y=4-s,Q[y]||(Q[y]=[]),H=P(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,H+="; newVal: "+qa(g)+"; oldVal: "+qa(f),Q[y].push(H));else if(d===c){x=!1;break a}}catch(w){p.$$phase=
|
||||
null,e(w)}if(!(h=L.$$childHead||L!==this&&L.$$nextSibling))for(;L!==this&&!(h=L.$$nextSibling);)L=L.$parent}while(L=h);if((x||k.length)&&!s--)throw p.$$phase=null,a("infdig",b,qa(Q));}while(x||k.length);for(p.$$phase=null;m.length;)try{m.shift()()}catch(T){e(T)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==p&&(q(this.$$listenerCount,eb(null,m,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&
|
||||
(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=C,this.$on=this.$watch=function(){return C})}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a){p.$$phase||
|
||||
p.$$asyncQueue.length||f.defer(function(){p.$$asyncQueue.length&&p.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return l("$apply"),this.$eval(a)}catch(b){e(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);
|
||||
var e=this;return function(){c[db(c,b)]=null;m(e,1,a)}},$emit:function(a,b){var c=[],d,g=this,f=!1,h={name:a,targetScope:g,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},l=[h].concat(ya.call(arguments,1)),k,m;do{d=g.$$listeners[a]||c;h.currentScope=g;k=0;for(m=d.length;k<m;k++)if(d[k])try{d[k].apply(null,l)}catch(n){e(n)}else d.splice(k,1),k--,m--;if(f)break;g=g.$parent}while(g);return h},$broadcast:function(a,b){for(var c=this,d=this,g={name:a,
|
||||
targetScope:this,preventDefault:function(){g.defaultPrevented=!0},defaultPrevented:!1},f=[g].concat(ya.call(arguments,1)),h,k;c=d;){g.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,f)}catch(l){e(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}return g}};var p=new h;return p}]}function bd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*(https?|ftp|file):|data:image\//;
|
||||
this.aHrefSanitizationWhitelist=function(a){return B(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return B(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,g;if(!S||8<=S)if(g=sa(c).href,""!==g&&!g.match(e))return"unsafe:"+g;return c}}}function Ae(b){if("self"===b)return b;if(w(b)){if(-1<b.indexOf("***"))throw ua("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+
|
||||
b+"$")}if(cb(b))return RegExp("^"+b.source+"$");throw ua("imatcher");}function Gc(b){var a=[];B(b)&&q(b,function(b){a.push(Ae(b))});return a}function ae(){this.SCE_CONTEXTS=ga;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=Gc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Gc(b));return a};this.$get=["$injector",function(c){function d(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=
|
||||
function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var e=function(a){throw ua("unsafe");};c.has("$sanitize")&&(e=c.get("$sanitize"));var g=d(),f={};f[ga.HTML]=d(g);f[ga.CSS]=d(g);f[ga.URL]=d(g);f[ga.JS]=d(g);f[ga.RESOURCE_URL]=d(f[ga.URL]);return{trustAs:function(a,b){var c=f.hasOwnProperty(a)?f[a]:null;if(!c)throw ua("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw ua("itype",a);return new c(b)},
|
||||
getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var g=f.hasOwnProperty(c)?f[c]:null;if(g&&d instanceof g)return d.$$unwrapTrustedValue();if(c===ga.RESOURCE_URL){var g=sa(d.toString()),m,n,p=!1;m=0;for(n=b.length;m<n;m++)if("self"===b[m]?Ib(g):b[m].exec(g.href)){p=!0;break}if(p)for(m=0,n=a.length;m<n;m++)if("self"===a[m]?Ib(g):a[m].exec(g.href)){p=!1;break}if(p)return d;throw ua("insecurl",d.toString());}if(c===ga.HTML)return e(d);throw ua("unsafe");},valueOf:function(a){return a instanceof
|
||||
g?a.$$unwrapTrustedValue():a}}}]}function $d(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw ua("iequirks");var e=ba(ga);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=Da);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,
|
||||
d(a,c))}};var g=e.parseAs,f=e.getTrusted,h=e.trustAs;q(ga,function(a,b){var c=K(b);e[Ta("parse_as_"+c)]=function(b){return g(a,b)};e[Ta("get_trusted_"+c)]=function(b){return f(a,b)};e[Ta("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function be(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(K((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),g=a[0]||{},f=g.documentMode,h,l=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=g.body&&g.body.style,
|
||||
m=!1,n=!1;if(k){for(var p in k)if(m=l.exec(p)){h=m[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");m=!!("transition"in k||h+"Transition"in k);n=!!("animation"in k||h+"Animation"in k);!d||m&&n||(m=w(g.body.style.webkitTransition),n=w(g.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!f||7<f),hasEvent:function(a){if("input"==a&&9==S)return!1;if(E(c[a])){var b=g.createElement("div");c[a]="on"+
|
||||
a in b}return c[a]},csp:Vb(),vendorPrefix:h,transitions:m,animations:n,android:d,msie:S,msieDocumentMode:f}}]}function de(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,l){var k=c.defer(),m=k.promise,n=B(l)&&!l;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete g[m.$$timeoutId]}n||b.$apply()},h);m.$$timeoutId=h;g[h]=k;return m}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),
|
||||
delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function sa(b,a){var c=b;S&&(W.setAttribute("href",c),c=W.href);W.setAttribute("href",c);return{href:W.href,protocol:W.protocol?W.protocol.replace(/:$/,""):"",host:W.host,search:W.search?W.search.replace(/^\?/,""):"",hash:W.hash?W.hash.replace(/^#/,""):"",hostname:W.hostname,port:W.port,pathname:"/"===W.pathname.charAt(0)?W.pathname:"/"+W.pathname}}function Ib(b){b=w(b)?sa(b):b;return b.protocol===Hc.protocol&&b.host===Hc.host}
|
||||
function ee(){this.$get=aa(O)}function gc(b){function a(d,e){if(X(d)){var g={};q(d,function(b,c){g[c]=a(c,b)});return g}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Ic);a("date",Jc);a("filter",Be);a("json",Ce);a("limitTo",De);a("lowercase",Ee);a("number",Kc);a("orderBy",Lc);a("uppercase",Fe)}function Be(){return function(b,a,c){if(!M(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;
|
||||
return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Ea.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&Fc.call(a,d)&&c(a[d],b[d]))return!0;return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var g=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!g(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,
|
||||
b);default:for(var d in a)if("$"!==d.charAt(0)&&g(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(g(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var f in a)(function(b){"undefined"!=typeof a[b]&&e.push(function(c){return g("$"==b?c:c&&c[b],a[b])})})(f);break;case "function":e.push(a);break;default:return b}d=[];for(f=0;f<b.length;f++){var h=b[f];e.check(h)&&d.push(h)}return d}}function Ic(b){var a=
|
||||
b.NUMBER_FORMATS;return function(b,d){E(d)&&(d=a.CURRENCY_SYM);return Mc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Kc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Mc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Mc(b,a,c,d,e){if(null==b||!isFinite(b)||X(b))return"";var g=0>b;b=Math.abs(b);var f=b+"",h="",l=[],k=!1;if(-1!==f.indexOf("e")){var m=f.match(/([\d\.]+)e(-?)(\d+)/);m&&"-"==m[2]&&m[3]>e+1?f="0":(h=f,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));
|
||||
else{f=(f.split(Nc)[1]||"").length;E(e)&&(e=Math.min(Math.max(a.minFrac,f),a.maxFrac));f=Math.pow(10,e);b=Math.round(b*f)/f;b=(""+b).split(Nc);f=b[0];b=b[1]||"";var m=0,n=a.lgSize,p=a.gSize;if(f.length>=n+p)for(m=f.length-n,k=0;k<m;k++)0===(m-k)%p&&0!==k&&(h+=c),h+=f.charAt(k);for(k=m;k<f.length;k++)0===(f.length-k)%n&&0!==k&&(h+=c),h+=f.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}l.push(g?a.negPre:a.posPre);l.push(h);l.push(g?a.negSuf:a.posSuf);return l.join("")}function Ob(b,
|
||||
a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function $(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Ob(e,a,d)}}function pb(b,a){return function(c,d){var e=c["get"+b](),g=Fa(a?"SHORT"+b:b);return d[g][e]}}function Jc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var g=0,f=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(g=Y(b[9]+b[10]),f=Y(b[9]+b[11]));
|
||||
h.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));g=Y(b[4]||0)-g;f=Y(b[5]||0)-f;h=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,g,f,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g="",f=[],h,l;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;w(c)&&(c=Ge.test(c)?Y(c):a(c));vb(c)&&(c=new Date(c));if(!Na(c))return c;for(;e;)(l=He.exec(e))?(f=f.concat(ya.call(l,1)),e=f.pop()):(f.push(e),e=null);q(f,function(a){h=
|
||||
Ie[a];g+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Ce(){return function(b){return qa(b,!0)}}function De(){return function(b,a){if(!M(b)&&!w(b))return b;a=Y(a);if(w(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Lc(b){return function(a,c,d){function e(a,b){return Qa(b)?function(b,c){return a(c,b)}:a}
|
||||
function g(a,b){var c=typeof a,d=typeof b;return c==d?("string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!M(a)||!c)return a;c=M(c)?c:[c];c=Uc(c,function(a){var c=!1,d=a||Da;if(w(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);d=b(a);if(d.constant){var f=d();return e(function(a,b){return g(a[f],b[f])},c)}}return e(function(a,b){return g(d(a),d(b))},c)});for(var f=[],h=0;h<a.length;h++)f.push(a[h]);return f.sort(e(function(a,b){for(var d=
|
||||
0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function va(b){P(b)&&(b={link:b});b.restrict=b.restrict||"AC";return aa(b)}function Oc(b,a,c,d){function e(a,c){c=c?"-"+fb(c,"-"):"";d.removeClass(b,(a?qb:rb)+c);d.addClass(b,(a?rb:qb)+c)}var g=this,f=b.parent().controller("form")||sb,h=0,l=g.$error={},k=[];g.$name=a.name||a.ngForm;g.$dirty=!1;g.$pristine=!0;g.$valid=!0;g.$invalid=!1;f.$addControl(g);b.addClass(La);e(!0);g.$addControl=function(a){Aa(a.$name,"input");k.push(a);a.$name&&
|
||||
(g[a.$name]=a)};g.$removeControl=function(a){a.$name&&g[a.$name]===a&&delete g[a.$name];q(l,function(b,c){g.$setValidity(c,!0,a)});Oa(k,a)};g.$setValidity=function(a,b,c){var d=l[a];if(b)d&&(Oa(d,c),d.length||(h--,h||(e(b),g.$valid=!0,g.$invalid=!1),l[a]=!1,e(!0,a),f.$setValidity(a,!0,g)));else{h||e(b);if(d){if(-1!=db(d,c))return}else l[a]=d=[],h++,e(!1,a),f.$setValidity(a,!1,g);d.push(c);g.$valid=!1;g.$invalid=!0}};g.$setDirty=function(){d.removeClass(b,La);d.addClass(b,tb);g.$dirty=!0;g.$pristine=
|
||||
!1;f.$setDirty()};g.$setPristine=function(){d.removeClass(b,tb);d.addClass(b,La);g.$dirty=!1;g.$pristine=!0;q(k,function(a){a.$setPristine()})}}function pa(b,a,c,d){b.$setValidity(a,c);return c?d:s}function Je(b,a,c){var d=c.prop("validity");X(d)&&b.$parsers.push(function(c){if(b.$error[a]||!(d.badInput||d.customError||d.typeMismatch)||d.valueMissing)return c;b.$setValidity(a,!1)})}function ub(b,a,c,d,e,g){var f=a.prop("validity");if(!e.android){var h=!1;a.on("compositionstart",function(a){h=!0});
|
||||
a.on("compositionend",function(){h=!1;l()})}var l=function(){if(!h){var e=a.val();Qa(c.ngTrim||"T")&&(e=ca(e));if(d.$viewValue!==e||f&&""===e&&!f.valueMissing)b.$$phase?d.$setViewValue(e):b.$apply(function(){d.$setViewValue(e)})}};if(e.hasEvent("input"))a.on("input",l);else{var k,m=function(){k||(k=g.defer(function(){l();k=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||m()});if(e.hasEvent("paste"))a.on("paste cut",m)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?
|
||||
"":d.$viewValue)};var n=c.ngPattern;n&&((e=n.match(/^\/(.*)\/([gim]*)$/))?(n=RegExp(e[1],e[2]),e=function(a){return pa(d,"pattern",d.$isEmpty(a)||n.test(a),a)}):e=function(c){var e=b.$eval(n);if(!e||!e.test)throw t("ngPattern")("noregexp",n,e,ha(a));return pa(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var p=Y(c.ngMinlength);e=function(a){return pa(d,"minlength",d.$isEmpty(a)||a.length>=p,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var r=
|
||||
Y(c.ngMaxlength);e=function(a){return pa(d,"maxlength",d.$isEmpty(a)||a.length<=r,a)};d.$parsers.push(e);d.$formatters.push(e)}}function Pb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],m=0;m<b.length;m++)if(e==b[m])continue a;c.push(e)}return c}function e(a){if(!M(a)){if(w(a))return a.split(" ");if(X(a)){var b=[];q(a,function(a,c){a&&b.push(c)});return b}}return a}return{restrict:"AC",link:function(g,f,h){function l(a,b){var c=
|
||||
f.data("$classCounts")||{},d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});f.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||g.$index%2===a){var k=e(b||[]);if(!m){var r=l(k,1);h.$addClass(r)}else if(!xa(b,m)){var q=e(m),r=d(k,q),k=d(q,k),k=l(k,-1),r=l(r,1);0===r.length?c.removeClass(f,k):0===k.length?c.addClass(f,r):c.setClass(f,r,k)}}m=ba(b)}var m;g.$watch(h[b],k,!0);h.$observe("class",function(a){k(g.$eval(h[b]))});"ngClass"!==b&&g.$watch("$index",
|
||||
function(c,d){var f=c&1;if(f!==d&1){var k=e(g.$eval(h[b]));f===a?(f=l(k,1),h.$addClass(f)):(f=l(k,-1),h.$removeClass(f))}})}}}]}var K=function(b){return w(b)?b.toLowerCase():b},Fc=Object.prototype.hasOwnProperty,Fa=function(b){return w(b)?b.toUpperCase():b},S,y,Ga,ya=[].slice,Ke=[].push,wa=Object.prototype.toString,Pa=t("ng"),Ea=O.angular||(O.angular={}),Sa,Ka,ka=["0","0","0"];S=Y((/msie (\d+)/.exec(K(navigator.userAgent))||[])[1]);isNaN(S)&&(S=Y((/trident\/.*; rv:(\d+)/.exec(K(navigator.userAgent))||
|
||||
[])[1]));C.$inject=[];Da.$inject=[];var ca=function(){return String.prototype.trim?function(b){return w(b)?b.trim():b}:function(b){return w(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();Ka=9>S?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Fa(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Xc=/[A-Z]/g,$c={full:"1.2.16",major:1,minor:2,dot:16,codeName:"badger-enumeration"},Ua=N.cache={},gb=N.expando="ng-"+(new Date).getTime(),
|
||||
me=1,Pc=O.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Fb=O.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};N._data=function(b){return this.cache[b[this.expando]]||{}};var he=/([\:\-\_]+(.))/g,ie=/^moz([A-Z])/,Bb=t("jqLite"),je=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Cb=/<|&#?\w+;/,ke=/<([\w:]+)/,le=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ea=
|
||||
{option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ea.optgroup=ea.option;ea.tbody=ea.tfoot=ea.colgroup=ea.caption=ea.thead;ea.th=ea.td;var Ja=N.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===U.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),N(O).on("load",a))},toString:function(){var b=
|
||||
[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0,push:Ke,sort:[].sort,splice:[].splice},kb={};q("multiple selected checked disabled readOnly required open".split(" "),function(b){kb[K(b)]=b});var nc={};q("input select option textarea button form details".split(" "),function(b){nc[Fa(b)]=!0});q({data:jc,inheritedData:jb,scope:function(b){return y(b).data("$scope")||jb(b.parentNode||b,["$isolateScope","$scope"])},
|
||||
isolateScope:function(b){return y(b).data("$isolateScope")||y(b).data("$isolateScopeNoTemplate")},controller:kc,injector:function(b){return jb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Gb,css:function(b,a,c){a=Ta(a);if(B(c))b.style[a]=c;else{var d;8>=S&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=S&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=K(a);if(kb[d])if(B(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));
|
||||
else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:s;else if(B(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?s:b},prop:function(b,a,c){if(B(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(E(d))return e?b[e]:"";b[e]=d}var a=[];9>S?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(E(a)){if("SELECT"===Ka(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&
|
||||
c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(E(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ha(d[c]);b.innerHTML=a},empty:lc},function(b,a){N.prototype[a]=function(a,d){var e,g;if(b!==lc&&(2==b.length&&b!==Gb&&b!==kc?a:d)===s){if(X(a)){for(e=0;e<this.length;e++)if(b===jc)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}e=b.$dv;g=e===s?Math.min(this.length,1):this.length;for(var f=0;f<g;f++){var h=b(this[f],a,d);e=
|
||||
e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:hc,dealoc:Ha,on:function a(c,d,e,g){if(B(g))throw Bb("onargs");var f=la(c,"events"),h=la(c,"handle");f||la(c,"events",f={});h||la(c,"handle",h=ne(c,f));q(d.split(" "),function(d){var g=f[d];if(!g){if("mouseenter"==d||"mouseleave"==d){var m=U.body.contains||U.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):
|
||||
a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};f[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||m(this,c))||h(a,d)})}else Pc(c,d,h),f[d]=[];g=f[d]}g.push(e)})},off:ic,one:function(a,c,d){a=y(a);a.on(c,function g(){a.off(c,d);a.off(c,g)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ha(a);q(new N(c),function(c){d?e.insertBefore(c,d.nextSibling):
|
||||
e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){q(new N(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new N(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ha(a);
|
||||
var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new N(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:ib,removeClass:hb,toggleClass:function(a,c,d){c&&q(c.split(" "),function(c){var g=d;E(g)&&(g=!Gb(a,c));(g?ib:hb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName?
|
||||
a.getElementsByTagName(c):[]},clone:Eb,triggerHandler:function(a,c,d){c=(la(a,"events")||{})[c];d=d||[];var e=[{preventDefault:C,stopPropagation:C}];q(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){N.prototype[c]=function(c,e,g){for(var f,h=0;h<this.length;h++)E(f)?(f=a(this[h],c,e,g),B(f)&&(f=y(f))):Db(f,a(this[h],c,e,g));return B(f)?f:this};N.prototype.bind=N.prototype.on;N.prototype.unbind=N.prototype.off});Va.prototype={put:function(a,c){this[Ia(a)]=c},get:function(a){return this[Ia(a)]},
|
||||
remove:function(a){var c=this[a=Ia(a)];delete this[a];return c}};var pe=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,qe=/,/,re=/^\s*(_?)(\S+?)\1\s*$/,oe=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Wa=t("$injector"),Le=t("$animate"),Ld=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Le("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?
|
||||
a:null);return this.$$classNameFilter};this.$get=["$timeout","$$asyncCallback",function(a,d){return{enter:function(a,c,f,h){f?f.after(a):(c&&c[0]||(c=f.parent()),c.append(a));h&&d(h)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,h){this.enter(a,c,d,h)},addClass:function(a,c,f){c=w(c)?c:M(c)?c.join(" "):"";q(a,function(a){ib(a,c)});f&&d(f)},removeClass:function(a,c,f){c=w(c)?c:M(c)?c.join(" "):"";q(a,function(a){hb(a,c)});f&&d(f)},setClass:function(a,c,f,h){q(a,function(a){ib(a,c);hb(a,
|
||||
f)});h&&d(h)},enabled:C}}]}],ja=t("$compile");cc.$inject=["$provide","$$sanitizeUriProvider"];var te=/^(x[\:\-_]|data[\:\-_])/i,vc=t("$interpolate"),Me=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,we={http:80,https:443,ftp:21},Kb=t("$location");Ac.prototype=Lb.prototype=zc.prototype={$$html5:!1,$$replace:!1,absUrl:nb("$$absUrl"),url:function(a,c){if(E(a))return this.$$url;var d=Me.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:nb("$$protocol"),
|
||||
host:nb("$$host"),port:nb("$$port"),path:Bc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(w(a))this.$$search=Yb(a);else if(X(a))this.$$search=a;else throw Kb("isrcharg");break;default:E(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Bc("$$hash",Da),replace:function(){this.$$replace=!0;return this}};var Ba=t("$parse"),Ec={},ta,Ma={"null":function(){return null},"true":function(){return!0},
|
||||
"false":function(){return!1},undefined:C,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return B(d)?B(e)?d+e:d:B(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(B(d)?d:0)-(B(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":C,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,
|
||||
c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Ne={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},
|
||||
Nb=function(a){this.options=a};Nb.prototype={constructor:Nb,lex:function(a){this.text=a;this.index=0;this.ch=s;this.lastCh=":";this.tokens=[];var c;for(a=[];this.index<this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent(),this.was("{,")&&("{"===a[0]&&(c=this.tokens[this.tokens.length-1]))&&(c.json=-1===c.text.indexOf("."));
|
||||
else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch,json:this.was(":[,")&&this.is("{[")||this.is("}]:,")}),this.is("{[")&&a.unshift(this.ch),this.is("}]")&&a.shift(),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{var d=this.ch+this.peek(),e=d+this.peek(2),g=Ma[this.ch],f=Ma[d],h=Ma[e];h?(this.tokens.push({index:this.index,text:e,fn:h}),this.index+=3):f?(this.tokens.push({index:this.index,text:d,fn:f}),this.index+=2):g?(this.tokens.push({index:this.index,
|
||||
text:this.ch,fn:g,json:this.was("[,:")&&this.is("+-")}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===
|
||||
a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=B(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw Ba("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=K(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=
|
||||
d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,json:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,g,f,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;
|
||||
this.index++}if(e)for(g=this.index;g<this.text.length;){h=this.text.charAt(g);if("("===h){f=c.substr(e-d+1);c=c.substr(0,e-d);this.index=g;break}if(this.isWhitespace(h))g++;else break}d={index:d,text:c};if(Ma.hasOwnProperty(c))d.fn=Ma[c],d.json=Ma[c];else{var l=Dc(c,this.options,this.text);d.fn=D(function(a,c){return l(a,c)},{assign:function(d,e){return ob(d,c,e,a.text,a.options)}})}this.tokens.push(d);f&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:f,json:!1}))},
|
||||
readString:function(a){var c=this.index;this.index++;for(var d="",e=a,g=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),e=e+f;if(g)"u"===f?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d=(g=Ne[f])?d+g:d+f,g=!1;else if("\\"===f)g=!0;else{if(f===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=
|
||||
f}this.index++}this.throwError("Unterminated quote",c)}};var $a=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};$a.ZERO=D(function(){return 0},{constant:!0});$a.prototype={constructor:$a,parse:function(a,c){this.text=a;this.json=c;this.tokens=this.lexer.lex(a);c&&(this.assignment=this.logicalOR,this.functionCall=this.fieldAccess=this.objectIndex=this.filterChain=function(){this.throwError("is not valid json",{text:a,index:0})});var d=c?this.primary():this.statements();0!==this.tokens.length&&
|
||||
this.throwError("is an unexpected token",this.tokens[0]);d.literal=!!d.literal;d.constant=!!d.constant;return d},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);c.json&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?
|
||||
(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw Ba("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw Ba("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var g=this.tokens[0],f=g.text;if(f===a||f===c||f===d||f===e||!(a||c||d||e))return g}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,
|
||||
e))?(this.json&&!a.json&&this.throwError("is not valid json",a),this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return D(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return D(function(e,g){return a(e,g)?c(e,g):d(e,g)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return D(function(e,g){return c(e,g,a,d)},{constant:a.constant&&d.constant})},
|
||||
statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,g=0;g<a.length;g++){var f=a[g];f&&(e=f(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=
|
||||
function(a,e,h){h=[h];for(var l=0;l<d.length;l++)h.push(d[l](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,g){return a.assign(d,c(d,g),g)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();
|
||||
if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},
|
||||
relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn($a.ZERO,a.fn,
|
||||
this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Dc(d,this.options,this.text);return D(function(c,d,h){return e(h||a(c,d))},{assign:function(e,f,h){return ob(a(e,h),d,f,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return D(function(e,g){var f=a(e,g),h=d(e,g),l;if(!f)return s;(f=Za(f[h],c.text))&&(f.then&&c.options.unwrapPromises)&&(l=f,"$$v"in f||(l.$$v=s,l.then(function(a){l.$$v=
|
||||
a})),f=f.$$v);return f},{assign:function(e,g,f){var h=d(e,f);return Za(a(e,f),c.text)[h]=g}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(g,f){for(var h=[],l=c?c(g,f):g,k=0;k<d.length;k++)h.push(d[k](g,f));k=a(g,f,l)||C;Za(l,e.text);Za(k,e.text);h=k.apply?k.apply(l,h):k(h[0],h[1],h[2],h[3],h[4]);return Za(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
|
||||
var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return D(function(c,d){for(var f=[],h=0;h<a.length;h++)f.push(a[h](c,d));return f},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return D(function(c,d){for(var e={},l=0;l<
|
||||
a.length;l++){var k=a[l];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Mb={},ua=t("$sce"),ga={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},W=U.createElement("a"),Hc=sa(O.location.href,!0);gc.$inject=["$provide"];Ic.$inject=["$locale"];Kc.$inject=["$locale"];var Nc=".",Ie={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:pb("Month"),MMM:pb("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",
|
||||
1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",1),sss:$("Milliseconds",3),EEEE:pb("Day"),EEE:pb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Ob(Math[0<a?"floor":"ceil"](a/60),2)+Ob(Math.abs(a%60),2))}},He=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,Ge=/^\-?\d+$/;Jc.$inject=["$locale"];var Ee=aa(K),Fe=aa(Fa);Lc.$inject=
|
||||
["$parse"];var cd=aa({restrict:"E",compile:function(a,c){8>=S&&(c.href||c.name||c.$set("href",""),a.append(U.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var g="[object SVGAnimatedString]"===wa.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(g)||a.preventDefault()})}}}),zb={};q(kb,function(a,c){if("multiple"!=a){var d=na("ng-"+c);zb[d]=function(){return{priority:100,link:function(a,g,f){a.$watch(f[d],function(a){f.$set(c,!!a)})}}}}});q(["src",
|
||||
"srcset","href"],function(a){var c=na("ng-"+a);zb[c]=function(){return{priority:99,link:function(d,e,g){var f=a,h=a;"href"===a&&"[object SVGAnimatedString]"===wa.call(e.prop("href"))&&(h="xlinkHref",g.$attr[h]="xlink:href",f=null);g.$observe(c,function(a){a&&(g.$set(h,a),S&&f&&e.prop(f,g[h]))})}}}});var sb={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C,$setPristine:C};Oc.$inject=["$element","$attrs","$scope","$animate"];var Qc=function(a){return["$timeout",function(c){return{name:"form",
|
||||
restrict:a?"EAC":"E",controller:Oc,compile:function(){return{pre:function(a,e,g,f){if(!g.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};Pc(e[0],"submit",h);e.on("$destroy",function(){c(function(){Fb(e[0],"submit",h)},0,!1)})}var l=e.parent().controller("form"),k=g.name||g.ngForm;k&&ob(a,k,f,k);if(l)e.on("$destroy",function(){l.$removeControl(f);k&&ob(a,k,s,k);D(f,sb)})}}}}}]},dd=Qc(),qd=Qc(!0),Oe=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,
|
||||
Pe=/^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i,Qe=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Rc={text:ub,number:function(a,c,d,e,g,f){ub(a,c,d,e,g,f);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Qe.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});Je(e,"number",c);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return pa(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),
|
||||
e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return pa(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return pa(e,"number",e.$isEmpty(a)||vb(a),a)})},url:function(a,c,d,e,g,f){ub(a,c,d,e,g,f);a=function(a){return pa(e,"url",e.$isEmpty(a)||Oe.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,f){ub(a,c,d,e,g,f);a=function(a){return pa(e,"email",e.$isEmpty(a)||Pe.test(a),a)};e.$formatters.push(a);
|
||||
e.$parsers.push(a)},radio:function(a,c,d,e){E(d.name)&&c.attr("name",bb());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,f=d.ngFalseValue;w(g)||(g=!0);w(f)||(f=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==g};
|
||||
e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:f})},hidden:C,button:C,submit:C,reset:C,file:C},dc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,f){f&&(Rc[K(g.type)]||Rc.text)(d,e,g,f,c,a)}}}],rb="ng-valid",qb="ng-invalid",La="ng-pristine",tb="ng-dirty",Re=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,g,f){function h(a,c){c=c?"-"+fb(c,"-"):"";f.removeClass(e,(a?qb:rb)+c);
|
||||
f.addClass(e,(a?rb:qb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var l=g(d.ngModel),k=l.assign;if(!k)throw t("ngModel")("nonassign",d.ngModel,ha(e));this.$render=C;this.$isEmpty=function(a){return E(a)||""===a||null===a||a!==a};var m=e.inheritedData("$formController")||sb,n=0,p=this.$error={};e.addClass(La);h(!0);this.$setValidity=function(a,c){p[a]!==
|
||||
!c&&(c?(p[a]&&n--,n||(h(!0),this.$valid=!0,this.$invalid=!1)):(h(!1),this.$invalid=!0,this.$valid=!1,n++),p[a]=!c,h(c,a),m.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=!0;f.removeClass(e,tb);f.addClass(e,La)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,f.removeClass(e,La),f.addClass(e,tb),m.$setDirty());q(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,k(a,d),q(this.$viewChangeListeners,
|
||||
function(a){try{a()}catch(d){c(d)}}))};var r=this;a.$watch(function(){var c=l(a);if(r.$modelValue!==c){var d=r.$formatters,e=d.length;for(r.$modelValue=c;e--;)c=d[e](c);r.$viewValue!==c&&(r.$viewValue=c,r.$render())}return c})}],Fd=function(){return{require:["ngModel","^?form"],controller:Re,link:function(a,c,d,e){var g=e[0],f=e[1]||sb;f.$addControl(g);a.$on("$destroy",function(){f.$removeControl(g)})}}},Hd=aa({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
|
||||
ec=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},Gd=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!E(a)){var c=[];a&&q(a.split(g),function(a){a&&
|
||||
c.push(ca(a))});return c}});e.$formatters.push(function(a){return M(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},Se=/^(true|false|\d+)$/,Id=function(){return{priority:100,compile:function(a,c){return Se.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a)})}}}},id=va(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),kd=["$interpolate",
|
||||
function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],jd=["$sce","$parse",function(a,c){return function(d,e,g){e.addClass("ng-binding").data("$binding",g.ngBindHtml);var f=c(g.ngBindHtml);d.$watch(function(){return(f(d)||"").toString()},function(c){e.html(a.getTrustedHtml(f(d))||"")})}}],ld=Pb("",!0),nd=Pb("Odd",0),md=Pb("Even",1),od=va({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),
|
||||
pd=[function(){return{scope:!0,controller:"@",priority:500}}],fc={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=na("ng-"+a);fc[c]=["$parse",function(d){return{compile:function(e,g){var f=d(g[c]);return function(c,d,e){d.on(K(a),function(a){c.$apply(function(){f(c,{$event:a})})})}}}}]});var sd=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",
|
||||
$$tlb:!0,link:function(c,d,e,g,f){var h,l,k;c.$watch(e.ngIf,function(g){Qa(g)?l||(l=c.$new(),f(l,function(c){c[c.length++]=U.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)})):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),h&&(k=yb(h.clone),a.leave(k,function(){k=null}),h=null))})}}}],td=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,g){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Ea.noop,compile:function(f,
|
||||
h){var l=h.ngInclude||h.src,k=h.onload||"",m=h.autoscroll;return function(f,h,q,s,u){var F=0,v,y,A,x=function(){y&&(y.remove(),y=null);v&&(v.$destroy(),v=null);A&&(e.leave(A,function(){y=null}),y=A,A=null)};f.$watch(g.parseAsResourceUrl(l),function(g){var l=function(){!B(m)||m&&!f.$eval(m)||d()},q=++F;g?(a.get(g,{cache:c}).success(function(a){if(q===F){var c=f.$new();s.template=a;a=u(c,function(a){x();e.enter(a,null,h,l)});v=c;A=a;v.$emit("$includeContentLoaded");f.$eval(k)}}).error(function(){q===
|
||||
F&&x()}),f.$emit("$includeContentRequested")):(x(),s.template=null)})}}}}],Jd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,g){d.html(g.template);a(d.contents())(c)}}}],ud=va({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),vd=va({terminal:!0,priority:1E3}),wd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,f){var h=f.count,l=f.$attr.when&&g.attr(f.$attr.when),k=f.offset||
|
||||
0,m=e.$eval(l)||{},n={},p=c.startSymbol(),r=c.endSymbol(),s=/^when(Minus)?(.+)$/;q(f,function(a,c){s.test(c)&&(m[K(c.replace("when","").replace("Minus","-"))]=g.attr(f.$attr[c]))});q(m,function(a,e){n[e]=c(a.replace(d,p+h+"-"+k+r))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in m||(c=a.pluralCat(c-k));return n[c](e,g,!0)},function(a){g.text(a)})}}}],xd=["$parse","$animate",function(a,c){var d=t("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,
|
||||
link:function(e,g,f,h,l){var k=f.ngRepeat,m=k.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,p,r,s,u,F,v={$id:Ia};if(!m)throw d("iexp",k);f=m[1];h=m[2];(m=m[3])?(n=a(m),p=function(a,c,d){F&&(v[F]=a);v[u]=c;v.$index=d;return n(e,v)}):(r=function(a,c){return Ia(c)},s=function(a){return a});m=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!m)throw d("iidexp",f);u=m[3]||m[1];F=m[2];var B={};e.$watchCollection(h,function(a){var f,h,m=g[0],n,v={},H,R,w,C,T,t,
|
||||
E=[];if(ab(a))T=a,n=p||r;else{n=p||s;T=[];for(w in a)a.hasOwnProperty(w)&&"$"!=w.charAt(0)&&T.push(w);T.sort()}H=T.length;h=E.length=T.length;for(f=0;f<h;f++)if(w=a===T?f:T[f],C=a[w],C=n(w,C,f),Aa(C,"`track by` id"),B.hasOwnProperty(C))t=B[C],delete B[C],v[C]=t,E[f]=t;else{if(v.hasOwnProperty(C))throw q(E,function(a){a&&a.scope&&(B[a.id]=a)}),d("dupes",k,C);E[f]={id:C};v[C]=!1}for(w in B)B.hasOwnProperty(w)&&(t=B[w],f=yb(t.clone),c.leave(f),q(f,function(a){a.$$NG_REMOVED=!0}),t.scope.$destroy());
|
||||
f=0;for(h=T.length;f<h;f++){w=a===T?f:T[f];C=a[w];t=E[f];E[f-1]&&(m=E[f-1].clone[E[f-1].clone.length-1]);if(t.scope){R=t.scope;n=m;do n=n.nextSibling;while(n&&n.$$NG_REMOVED);t.clone[0]!=n&&c.move(yb(t.clone),null,y(m));m=t.clone[t.clone.length-1]}else R=e.$new();R[u]=C;F&&(R[F]=w);R.$index=f;R.$first=0===f;R.$last=f===H-1;R.$middle=!(R.$first||R.$last);R.$odd=!(R.$even=0===(f&1));t.scope||l(R,function(a){a[a.length++]=U.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,y(m));m=a;t.scope=R;t.clone=
|
||||
a;v[t.id]=t})}B=v})}}}],yd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Qa(c)?"removeClass":"addClass"](d,"ng-hide")})}}],rd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Qa(c)?"addClass":"removeClass"](d,"ng-hide")})}}],zd=va(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Ad=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases=
|
||||
{}}],link:function(c,d,e,g){var f,h,l,k=[];c.$watch(e.ngSwitch||e.on,function(d){var n,p=k.length;if(0<p){if(l){for(n=0;n<p;n++)l[n].remove();l=null}l=[];for(n=0;n<p;n++){var r=h[n];k[n].$destroy();l[n]=r;a.leave(r,function(){l.splice(n,1);0===l.length&&(l=null)})}}h=[];k=[];if(f=g.cases["!"+d]||g.cases["?"])c.$eval(e.change),q(f,function(d){var e=c.$new();k.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],Bd=va({transclude:"element",priority:800,require:"^ngSwitch",
|
||||
link:function(a,c,d,e,g){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:g,element:c})}}),Cd=va({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,g){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:g,element:c})}}),Ed=va({link:function(a,c,d,e,g){if(!g)throw t("ngTransclude")("orphan",ha(c));g(function(a){c.empty();c.append(a)})}}),ed=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,
|
||||
d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],Te=t("ngOptions"),Dd=aa({terminal:!0}),fd=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,e={$setViewValue:C};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var l=this,k={},m=e,n;l.databound=
|
||||
d.ngModel;l.init=function(a,c,d){m=a;n=d};l.addOption=function(c){Aa(c,'"option value"');k[c]=!0;m.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};l.removeOption=function(a){this.hasOption(a)&&(delete k[a],m.$viewValue==a&&this.renderUnknownOption(a))};l.renderUnknownOption=function(c){c="? "+Ia(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",!0)};l.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){l.renderUnknownOption=C})}],link:function(e,f,h,l){function k(a,
|
||||
c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(A.parent()&&A.remove(),c.val(a),""===a&&w.prop("selected",!0)):E(a)&&w?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){A.parent()&&A.remove();d.$setViewValue(c.val())})})}function m(a,c,d){var e;d.$render=function(){var a=new Va(d.$viewValue);q(c.find("option"),function(c){c.selected=B(a.get(c.value))})};a.$watch(function(){xa(e,d.$viewValue)||(e=ba(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=
|
||||
[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k,s,t,z;t=g.$modelValue;z=y(e)||[];var E=n?Qb(z):z,F,I,A;I={};s=!1;var D,H;if(r)if(w&&M(t))for(s=new Va([]),A=0;A<t.length;A++)I[m]=t[A],s.put(w(e,I),t[A]);else s=new Va(t);for(A=0;F=E.length,A<F;A++){k=A;if(n){k=E[A];if("$"===k.charAt(0))continue;I[n]=k}I[m]=z[k];d=p(e,I)||"";(k=a[d])||(k=a[d]=[],c.push(d));r?d=B(s.remove(w?w(e,I):q(e,I))):(w?(d={},d[m]=t,d=
|
||||
w(e,d)===w(e,I)):d=t===q(e,I),s=s||d);D=l(e,I);D=B(D)?D:"";k.push({id:w?w(e,I):n?E[A]:A,label:D,selected:d})}r||(u||null===t?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));I=0;for(E=c.length;I<E;I++){d=c[I];k=a[d];x.length<=I?(t={element:C.clone().attr("label",d),label:k.label},z=[t],x.push(z),f.append(t.element)):(z=x[I],t=z[0],t.label!=d&&t.element.attr("label",t.label=d));D=null;A=0;for(F=k.length;A<F;A++)s=k[A],(d=z[A+1])?(D=d.element,d.label!==s.label&&
|
||||
D.text(d.label=s.label),d.id!==s.id&&D.val(d.id=s.id),d.selected!==s.selected&&D.prop("selected",d.selected=s.selected)):(""===s.id&&u?H=u:(H=v.clone()).val(s.id).attr("selected",s.selected).text(s.label),z.push({element:H,label:s.label,id:s.id,selected:s.selected}),D?D.after(H):t.element.append(H),D=H);for(A++;z.length>A;)z.pop().element.remove()}for(;x.length>I;)x.pop()[0].element.remove()}var k;if(!(k=t.match(d)))throw Te("iexp",t,ha(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],p=c(k[3]||""),q=
|
||||
c(k[2]?k[1]:m),y=c(k[7]),w=k[8]?c(k[8]):null,x=[[{element:f,label:""}]];u&&(a(u)(e),u.removeClass("ng-scope"),u.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=y(e)||[],d={},h,k,l,p,t,v,u;if(r)for(k=[],p=0,v=x.length;p<v;p++)for(a=x[p],l=1,t=a.length;l<t;l++){if((h=a[l].element)[0].selected){h=h.val();n&&(d[n]=h);if(w)for(u=0;u<c.length&&(d[m]=c[u],w(e,d)!=h);u++);else d[m]=c[h];k.push(q(e,d))}}else{h=f.val();if("?"==h)k=s;else if(""===h)k=null;else if(w)for(u=0;u<c.length;u++){if(d[m]=
|
||||
c[u],w(e,d)==h){k=q(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=q(e,d);1<x[0].length&&x[0][1].id!==h&&(x[0][1].selected=!1)}g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(l[1]){var p=l[0];l=l[1];var r=h.multiple,t=h.ngOptions,u=!1,w,v=y(U.createElement("option")),C=y(U.createElement("optgroup")),A=v.clone();h=0;for(var x=f.children(),D=x.length;h<D;h++)if(""===x[h].value){w=u=x.eq(h);break}p.init(l,u,A);r&&(l.$isEmpty=function(a){return!a||0===a.length});t?n(e,f,l):r?m(e,f,l):k(e,f,l,p)}}}}],hd=["$interpolate",
|
||||
function(a){var c={addOption:C,removeOption:C};return{restrict:"E",priority:100,compile:function(d,e){if(E(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),m=k.data("$selectController")||k.parent().data("$selectController");m&&m.databound?d.prop("selected",!1):m=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&m.removeOption(c);m.addOption(a)}):m.addOption(e.value);d.on("$destroy",function(){m.removeOption(e.value)})}}}}],gd=aa({restrict:"E",
|
||||
terminal:!0});O.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):((Ga=O.jQuery)?(y=Ga,D(Ga.fn,{scope:Ja.scope,isolateScope:Ja.isolateScope,controller:Ja.controller,injector:Ja.injector,inheritedData:Ja.inheritedData}),Ab("remove",!0,!0,!1),Ab("empty",!1,!1,!1),Ab("html",!1,!1,!0)):y=N,Ea.element=y,Zc(Ea),y(U).ready(function(){Wc(U,$b)}))})(window,document);!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "angular",
|
||||
"version": "1.2.16",
|
||||
"main": "./angular.js",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "ionic",
|
||||
"version": "1.0.0-beta.4",
|
||||
"codename": "antimony-antelope",
|
||||
"homepage": "https://github.com/driftyco/ionic",
|
||||
"authors": [
|
||||
"Max Lynch <max@drifty.com>",
|
||||
"Adam Bradley <adam@drifty.com>",
|
||||
"Ben Sperry <ben@drifty.com>"
|
||||
],
|
||||
"description": "Advanced HTML5 hybrid mobile app development framework.",
|
||||
"main": [
|
||||
"release/css/ionic.css",
|
||||
"release/fonts/*",
|
||||
"release/js/ionic.js",
|
||||
"release/js/ionic-angular.js"
|
||||
],
|
||||
"keywords": [
|
||||
"mobile",
|
||||
"html5",
|
||||
"ionic",
|
||||
"cordova",
|
||||
"phonegap",
|
||||
"trigger",
|
||||
"triggerio",
|
||||
"angularjs",
|
||||
"angular"
|
||||
],
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"CONTRIBUTING.md",
|
||||
"Gruntfile.js",
|
||||
"component.json",
|
||||
"config",
|
||||
"examples",
|
||||
"package.json",
|
||||
"release/js/angular",
|
||||
"release/js/angular-ui",
|
||||
"scripts",
|
||||
"test",
|
||||
"js/controllers",
|
||||
"js/ext",
|
||||
"js/utils",
|
||||
"js/views",
|
||||
"js/_license.js",
|
||||
"js/ionic.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.2.12",
|
||||
"angular-animate": "~1.2.12",
|
||||
"angular-sanitize": "~1.2.12",
|
||||
"angular-ui-router": "0.2.7"
|
||||
},
|
||||
"_release": "1.0.0-beta.4",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.0.0-beta.4",
|
||||
"commit": "db8361ddc05111dbbc0a7f9c061204ef71f25055"
|
||||
},
|
||||
"_source": "git://github.com/driftyco/ionic.git",
|
||||
"_target": "v1.0.0-beta.4",
|
||||
"_originalSource": "ionic"
|
||||
}
|
||||
@@ -0,0 +1,910 @@
|
||||
<a name="1.0.0-beta.4"></a>
|
||||
### 1.0.0-beta.4 "americium" (2014-05-07)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **.pane:** Remove overflow:hidden from .pane,.view ([64f0030b](https://github.com/driftyco/ionic/commit/64f0030b99ba83772d698252470894740e270f9b))
|
||||
* **.tabs-icon-*:** only work on directly descendant tabs ([77f26831](https://github.com/driftyco/ionic/commit/77f268312468b8272d009bd68b2e408316e00dc0), closes [#1261](https://github.com/driftyco/ionic/issues/1261))
|
||||
* **bar:** properly align titles if switching to view with no buttons ([ade143ed](https://github.com/driftyco/ionic/commit/ade143ed35a4a17e5ffffaec17b5529eae3b1dde), closes [#1242](https://github.com/driftyco/ionic/issues/1242))
|
||||
* **collectionRepeat:** correctly save user scroll position on back ([0a640758](https://github.com/driftyco/ionic/commit/0a640758842d4307dba43aa702fe8d9dc17b164a))
|
||||
* **ionItem:** fix error when repeating ([f370db45](https://github.com/driftyco/ionic/commit/f370db45bc9cce3aee3d8b3dccad6f38dd203a20))
|
||||
* **ionSlideBox:**
|
||||
* fix disable-scroll attr, deprecate in favor of $ionicSlideBoxDelegate.enableSlide(true/false) ([1bdb5e8d](https://github.com/driftyco/ionic/commit/1bdb5e8d9f1798fcd0acbf7cce6bd7b6166a0096), closes [#1113](https://github.com/driftyco/ionic/issues/1113))
|
||||
* prevent NPE during drag ([920dc59d](https://github.com/driftyco/ionic/commit/920dc59d75acdcc0a109a6731088b3d3cff09d85), closes [#1240](https://github.com/driftyco/ionic/issues/1240))
|
||||
* **modal:** Remove modal flicker, closes #1150 ([d2ebed84](https://github.com/driftyco/ionic/commit/d2ebed847e0214e4654337947c8d3c15ee7c87f9))
|
||||
* **scrollView:** recalculate size on mousewheel scroll ([89a9ed15](https://github.com/driftyco/ionic/commit/89a9ed1547010d90591798625eeaea973aaf6c20))
|
||||
* **select:** Select options w/ mouse events, closes #1251 ([e3306293](https://github.com/driftyco/ionic/commit/e3306293cc62ff9ef931b4dc6d0b76c61ab247a2))
|
||||
* **tap:** input[file] clicks within ion-content, closes #1237 ([05a6d7cc](https://github.com/driftyco/ionic/commit/05a6d7cca660e7f5b3b2ee4b5698dfb33df7b605))
|
||||
* **toggle:** Right side padding for item-complex, closes #1091 ([45106a6a](https://github.com/driftyco/ionic/commit/45106a6acfedf8b77a0f46d9f0b1b35e4a76c548))
|
||||
* **transitions:** Disable transitions on Android 2, closes #780 ([9c58d47b](https://github.com/driftyco/ionic/commit/9c58d47b2b7c4d5695c1eb1b2ee7d23555ae0137))
|
||||
* **viewport:**
|
||||
* Auto update viewport tag ([5f8e9040](https://github.com/driftyco/ionic/commit/5f8e9040bd8eb8dfef0db5bfef9275ef4a51f0da))
|
||||
* Remove height value on iOS browser ([0ad10ede](https://github.com/driftyco/ionic/commit/0ad10edefcdcc67d20fb837f635609974af5dbd7))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$ionicLoading:** on android, no back button action while loading ([fc8711c7](https://github.com/driftyco/ionic/commit/fc8711c7d000d06bfc30fcf813b2b9e26c228be5), closes [#1273](https://github.com/driftyco/ionic/issues/1273))
|
||||
* **$ionicModal:** close on backdrop click on desktop/tablet ([554c4398](https://github.com/driftyco/ionic/commit/554c43980e555d4506b4035ed100aca22c51da0c), closes [#1087](https://github.com/driftyco/ionic/issues/1087))
|
||||
* **ionNavBar:** allow navbar inside ion-view to transition whole bar ([42177c3b](https://github.com/driftyco/ionic/commit/42177c3b939f6fd2dafb7b6c30bce4ef31540411), closes [#1232](https://github.com/driftyco/ionic/issues/1232))
|
||||
* **sideMenu:** make android back button close side menu ([10103559](https://github.com/driftyco/ionic/commit/101035593f520ded77d61013d0330f537d270168), closes [#1264](https://github.com/driftyco/ionic/issues/1264))
|
||||
|
||||
|
||||
<a name="1.0.0-beta.3"></a>
|
||||
### 1.0.0-beta.3 "americium" (2014-04-30)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$ionicModal:** do not inherit has-header etc from parent scope ([6c0c8350](https://github.com/driftyco/ionic/commit/6c0c8350f2d925a91bcf1460dc58c7beb8c97922), closes [#1191](https://github.com/driftyco/ionic/issues/1191))
|
||||
* **.pane:** make pane class have overflow: hidden; fixes scrolling whole page from header/footer errors ([dca87e2c](https://github.com/driftyco/ionic/commit/dca87e2c5f8992c0b38ac02e3e71b73b6e18388d), closes [#1218](https://github.com/driftyco/ionic/issues/1218))
|
||||
* **android-2.2:** use string notation for reserved 'class' keyword ([3bcf06f2](https://github.com/driftyco/ionic/commit/3bcf06f217582922bc16af198597ccc17274b929), closes [#1228](https://github.com/driftyco/ionic/issues/1228))
|
||||
* **collectionRepeat:** allow percent expressions ([94bcbf75](https://github.com/driftyco/ionic/commit/94bcbf753bdefd2b7ff5ac34d1eb594af5bfb02d))
|
||||
* **infiniteScroll:** allow to fire if list does not fill up screen ([e35b95e1](https://github.com/driftyco/ionic/commit/e35b95e1238038787def75c1cb9bf4cbb524d327))
|
||||
* **ionHeaderBar:** have no side effects with content in other views ([7fd31b6a](https://github.com/driftyco/ionic/commit/7fd31b6aed4dbe132566071f54b5a7cbf6ab46cb), closes [#1095](https://github.com/driftyco/ionic/issues/1095))
|
||||
* **ionList:** make reorder follow an offset list as it scrolls ([3a68a2c9](https://github.com/driftyco/ionic/commit/3a68a2c9022e586c7c313418be8c05005dc5c682))
|
||||
* **ionRadio:** pass name property down to input element ([9995f46b](https://github.com/driftyco/ionic/commit/9995f46b223406fdf4a45270f185eacb5135bc37), closes [#1229](https://github.com/driftyco/ionic/issues/1229))
|
||||
* **ionTab:** make it so tabNav works with ngRepeat ([288d4ac2](https://github.com/driftyco/ionic/commit/288d4ac230a00898b9388fdb6e6caf386dfb840d))
|
||||
* **ionView:**
|
||||
* initialize hideBack/hideNav to false if undefined ([5e56c2d6](https://github.com/driftyco/ionic/commit/5e56c2d6b78dbb8943adc738dc214ffef066e654))
|
||||
* only $watch attributes if defined ([12e5f6c1](https://github.com/driftyco/ionic/commit/12e5f6c1162adc1094026c5539932a66f5767fb9), closes [#1216](https://github.com/driftyco/ionic/issues/1216))
|
||||
* **scrollView:** stop polluting global.core ([8992e7c9](https://github.com/driftyco/ionic/commit/8992e7c903543340b50f3074a43eff3e1b884b45))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$ionicActionSheet:** allow html binding of buttons and title ([3e5b39f2](https://github.com/driftyco/ionic/commit/3e5b39f20175fbe16d7439bdfbeb508544782fbf), closes [#1219](https://github.com/driftyco/ionic/issues/1219))
|
||||
* **$ionicPopup:** on android, make back button close popup ([b87bcb30](https://github.com/driftyco/ionic/commit/b87bcb30c33fb577b41139c43089b2a864211d56), closes [#1222](https://github.com/driftyco/ionic/issues/1222))
|
||||
* **$ionicSlideBoxDelegate:** add enableSlide(true/false) method ([e003bf18](https://github.com/driftyco/ionic/commit/e003bf18bc0d771c85ae124e89f0ad2dad0aae71), closes [#1122](https://github.com/driftyco/ionic/issues/1122))
|
||||
|
||||
|
||||
<a name="1.0.0-beta.2"></a>
|
||||
### 1.0.0-beta.2 "aluminum" (2014-04-28)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$ionicActionSheet:** provide default functions for options ([ba39fb00](https://github.com/driftyco/ionic/commit/ba39fb0046cf9ff8b3d18fb8ac7d10d734afe192), closes [#1013](https://github.com/driftyco/ionic/issues/1013))
|
||||
* **$ionicLoading:** stop race condition with show and hide ([eb1dee93](https://github.com/driftyco/ionic/commit/eb1dee9303177d47315a02cf327e909c14c05b22), closes [#1100](https://github.com/driftyco/ionic/issues/1100))
|
||||
* **$ionicModal:** make it use jqLite#remove() ([104c6420](https://github.com/driftyco/ionic/commit/104c642019a05ef70dee8de84a51b99272266d63), closes [#1138](https://github.com/driftyco/ionic/issues/1138))
|
||||
* **$ionicPopup:**
|
||||
* stop race condition with show then hide ([698c93fc](https://github.com/driftyco/ionic/commit/698c93fcaf5def44cc01783faa6e188c15379e40))
|
||||
* make it work if jquery is included ([21ff7547](https://github.com/driftyco/ionic/commit/21ff75479f2dbc6cc4824732d7edc79e1d138491), closes [#1027](https://github.com/driftyco/ionic/issues/1027))
|
||||
* **active:** Taps/clicks now use .activated instead of .active, closes #913 ([870dcd6f](https://github.com/driftyco/ionic/commit/870dcd6f992f62c1a34258039d2a427490aa1b6d))
|
||||
* **bar:** Increased button icon font size ([8847774f](https://github.com/driftyco/ionic/commit/8847774f2aa6ce6eb4747d3ce1fe7a49e4bc8502))
|
||||
* **checkbox:**
|
||||
* Remove tap highlight and fix checkbox appearance in Firefox, closes #496 ([b0b446d5](https://github.com/driftyco/ionic/commit/b0b446d5d93cbfca027c828bc38bf827b998b6c5))
|
||||
* stretch checkbox's clickable area to full width ([5238f8f3](https://github.com/driftyco/ionic/commit/5238f8f3dd6d527814f67fc5aa1ad003dc04c160))
|
||||
* Entire row of an .item w/ a checkbox is now tappable, closes #995 ([ac94bb23](https://github.com/driftyco/ionic/commit/ac94bb236ecb9646d406def39b56a2a099e76d28))
|
||||
* **header:** Header input too long on Android 4.2, closes #1081 ([b654e02e](https://github.com/driftyco/ionic/commit/b654e02e0a8b852875509753eb106f60c11fa3c4))
|
||||
* **input:** Fix to hide input overlays on old Android ([0e9072e4](https://github.com/driftyco/ionic/commit/0e9072e48123e3542680047d9378b9418a7a3311))
|
||||
* **ion-scroll:**
|
||||
* removed 100% height ([f0d33981](https://github.com/driftyco/ionic/commit/f0d339812f869e919b6a434eb5674f3d06a518c1))
|
||||
* added display block ([d295aee4](https://github.com/driftyco/ionic/commit/d295aee416aa89a1284f015ed5f65d81a95b6173))
|
||||
* **ionContent:**
|
||||
* fix scoping with ngController ([6abce8f7](https://github.com/driftyco/ionic/commit/6abce8f771f0e6eeae3e6e7d87f11bd3f2318dcc), closes [#1155](https://github.com/driftyco/ionic/issues/1155))
|
||||
* do not let child scopes inherit has-* classes ([a5eb48b9](https://github.com/driftyco/ionic/commit/a5eb48b9569d3c9f90c238a7ce6f33b5753266f7), closes [#924](https://github.com/driftyco/ionic/issues/924))
|
||||
* **ionHeaderBar:**
|
||||
* do not tapScrollToTop for inputs ([5722900a](https://github.com/driftyco/ionic/commit/5722900a5851d8037ab933e16c94569efc11f476), closes [#1199](https://github.com/driftyco/ionic/issues/1199))
|
||||
* make it align after elements properly load ([d00aaa59](https://github.com/driftyco/ionic/commit/d00aaa59455fdd6b4e34ac4547e86e1049734954))
|
||||
* **ionInfiniteScroll:**
|
||||
* remove listener on $destroy ([08da6f75](https://github.com/driftyco/ionic/commit/08da6f753c399a3e9fcf66bdc9008f9f6cb3e227))
|
||||
* work properly if past horizontal boundaries ([d58fff72](https://github.com/driftyco/ionic/commit/d58fff72ded2a74c1d4e757adfb256b7e74fb578), closes [#1073](https://github.com/driftyco/ionic/issues/1073))
|
||||
* **ionList:**
|
||||
* disable swiping of items while option buttons are shown ([81676e6e](https://github.com/driftyco/ionic/commit/81676e6ef798422fed45b461bf80d1ebeef9fc33))
|
||||
* do not let option button click propagate to item ([a845ff34](https://github.com/driftyco/ionic/commit/a845ff348943beba2fd4758744f7ee7b865b4ade))
|
||||
* disable tap on element being edited ([634b3971](https://github.com/driftyco/ionic/commit/634b3971b1456744ccd4321600f8d50099245a56))
|
||||
* make reorder position work if list is offset ([90da2da6](https://github.com/driftyco/ionic/commit/90da2da66f87813afe9ee4a1727cbc423af77a64))
|
||||
* show reorder/delete on item creation if list is showing ([09a77299](https://github.com/driftyco/ionic/commit/09a7729914ee3e24fe12535f223f9f755c8791e1), closes [#1181](https://github.com/driftyco/ionic/issues/1181))
|
||||
* **ionNavBar:** adjust has-header if ionNavBar is hidden ([41b73abf](https://github.com/driftyco/ionic/commit/41b73abf40a34d1c96245a2d73d977a9be05d6ea), closes [#927](https://github.com/driftyco/ionic/issues/927))
|
||||
* **ionNavButtons:**
|
||||
* fix side="left" flicker with back button on ios ([b6266889](https://github.com/driftyco/ionic/commit/b6266889a43cc5b71b4520f8b43d5cc73139b3f3))
|
||||
* multiple ionNavButtons elements align correctly ([58de2671](https://github.com/driftyco/ionic/commit/58de267171790eb7ebfb8163ebc9523ce10b7e49), closes [#930](https://github.com/driftyco/ionic/issues/930))
|
||||
* **ionRefresher:** do not animate pulling-text ([5c893ab8](https://github.com/driftyco/ionic/commit/5c893ab81acf2af8442b321ad1c9bfd47e1c7981), closes [#909](https://github.com/driftyco/ionic/issues/909))
|
||||
* **ionTab:** make sure all tab-nav attributes are re-interpolated on change ([757f1819](https://github.com/driftyco/ionic/commit/757f1819313de58a3d419d0ec6075c80e40f3d61), closes [#955](https://github.com/driftyco/ionic/issues/955), [#1071](https://github.com/driftyco/ionic/issues/1071))
|
||||
* **ionView:** do not set navbar title if no title attr set ([d53eab81](https://github.com/driftyco/ionic/commit/d53eab819728e56ec3ee9a0906b79e277c30c07e), closes [#915](https://github.com/driftyco/ionic/issues/915))
|
||||
* **ionicPopup:** if input exists, focus it. else, focus first button ([93aa16a7](https://github.com/driftyco/ionic/commit/93aa16a78ba9732a3b47d3b952c1a58db755cc3b), closes [#1176](https://github.com/driftyco/ionic/issues/1176))
|
||||
* **ionicTabBar:** detect if matches state in all cases ([ee2b7686](https://github.com/driftyco/ionic/commit/ee2b76864abfecbb1f8aa53c70339e82d22a23b3))
|
||||
* **item:**
|
||||
* Vertically align nav icon w/out flexbox to prevent android crashes, #928 ([5b0f5d02](https://github.com/driftyco/ionic/commit/5b0f5d024c191f76b368858961d1c711d1eaa3ab))
|
||||
* Fix css overflow overrides for .item-text-wrap ([04b4d771](https://github.com/driftyco/ionic/commit/04b4d771c616a3884d127183bd12cf85f23232ab))
|
||||
* Fix delete icon alignment in .item-icon.left, closes #946 ([044211de](https://github.com/driftyco/ionic/commit/044211def34cc57e0b1a90e8541640b640a2de75))
|
||||
* **labels:** Fix .input-label's width for androids w/out full flex box support, #998 ([096a01c1](https://github.com/driftyco/ionic/commit/096a01c117e4ba0eafc17af3f0992c5ff10008bd))
|
||||
* **listView:** fixed active state on scroll ([040af824](https://github.com/driftyco/ionic/commit/040af8245d42fd17c7f2bd5bce5438330a90cf95))
|
||||
* **loading:** fix not hiding after two shows, always cancel delay ([4216266f](https://github.com/driftyco/ionic/commit/4216266f2108f0b3c348b0888d37a59440099be4), closes [#1130](https://github.com/driftyco/ionic/issues/1130))
|
||||
* **modal:** Increase delay of removing .modal-open to prevent focus under modals ([83fd11c5](https://github.com/driftyco/ionic/commit/83fd11c5655e38226029fc00c43b921430fb6b32))
|
||||
* **navClear:**
|
||||
* only set viewOptions if click leads to state change ([4dffc5f6](https://github.com/driftyco/ionic/commit/4dffc5f6c646ee55a44c818aca9e635ea0934a3c), closes [#1043](https://github.com/driftyco/ionic/issues/1043))
|
||||
* be sure it runs before ngClick ([4f47bf24](https://github.com/driftyco/ionic/commit/4f47bf24f05c4f5007bad96c37d3a566ad50cb39), closes [#1047](https://github.com/driftyco/ionic/issues/1047))
|
||||
* **popup:** Backwards compatible remove popup from DOM, closes #851 ([441a21c4](https://github.com/driftyco/ionic/commit/441a21c4949827a52d1a6edff58f1cb786744acb))
|
||||
* **range:** Fix range being able to slide when in a side menu, closes #318 ([2fbdebcd](https://github.com/driftyco/ionic/commit/2fbdebcdea97ba2896106b80ca8c061588434c66))
|
||||
* **refresher:** get rid of flickers except on droid-4.4 ([ad671848](https://github.com/driftyco/ionic/commit/ad67184800a22a8d27e3859298cc3f7e83b2a25c), closes [#1014](https://github.com/driftyco/ionic/issues/1014))
|
||||
* **scroll:**
|
||||
* Allow scrolling when touchstart target is an input, #1078 ([8af018b1](https://github.com/driftyco/ionic/commit/8af018b1facff1dee753f0cf4c341f0a91285d6a))
|
||||
* Fix input focus when tapped, do not scroll when target is an input, closes #1020 ([66ecec70](https://github.com/driftyco/ionic/commit/66ecec7031ae472351da6a192364097e889d96a8))
|
||||
* Do not ignore taps if the target was an input, #997 ([e6f56237](https://github.com/driftyco/ionic/commit/e6f562377d124db4018d5664d4da6d41e7587230))
|
||||
* Fix scroll for devices w/out dataset support, closes #976 ([bfcf2650](https://github.com/driftyco/ionic/commit/bfcf26507f0f0c50405a6e48761f2a4eb76c5c02))
|
||||
* **scss:** make tabs,header,footer aligned right with statusbar ([c2a38a6d](https://github.com/driftyco/ionic/commit/c2a38a6d3d105319a0462d90464562be3296295a))
|
||||
* **tabs:** Tab icon align within nested tabs, closes #1093 ([2a6f7029](https://github.com/driftyco/ionic/commit/2a6f7029805596e56373213cf3c4139cc7d7d70c))
|
||||
* **tabs-item-hide:** only hide tab nav items ([a7eb521c](https://github.com/driftyco/ionic/commit/a7eb521c90fa7c8c2412308951fd04a87544683f))
|
||||
* **tap:**
|
||||
* Do not preventDefault after input focus, #1068 ([a977332f](https://github.com/driftyco/ionic/commit/a977332f2b847262a406cf1fbedb2c3bb6b560eb))
|
||||
* Remove 300ms delay when tapping select elements ([cf686548](https://github.com/driftyco/ionic/commit/cf686548daf3c33ada2fff7de048aeb2ce3c61a7))
|
||||
* Prevent clicks from firing after scrolling, #579 ([cb602b58](https://github.com/driftyco/ionic/commit/cb602b587bfb4e9c92ef9f0542927aafeb39eef1))
|
||||
* Deactivate elements during scroll at the same time click is ignored, #997 ([3ee5ea77](https://github.com/driftyco/ionic/commit/3ee5ea77a64f05423d34fbe6da8a4c4fcf0fdb6c))
|
||||
* Do not simulate a click if it was from a touchcanel event, closes #1015 ([78510099](https://github.com/driftyco/ionic/commit/78510099add6d65a925c497ab9303560474a1807))
|
||||
* Prevent multiple clicks when overriding cordova object, closes #1022 ([5f3a1d21](https://github.com/driftyco/ionic/commit/5f3a1d21c6352e51f450dbdacace1e527048698f))
|
||||
* Prevent "clicking" when scrolling ([f3bd258c](https://github.com/driftyco/ionic/commit/f3bd258c9ee39503c9d34d85ad31c754fff0c03c))
|
||||
* Increate isScrolledSinceStart from 2px radius to 15px, #970 ([9a49129a](https://github.com/driftyco/ionic/commit/9a49129aa090caf221f0c0b2a77f86b4a69ab652))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$ionicBackdrop:** add backdrop show/hide service ([730a33b9](https://github.com/driftyco/ionic/commit/730a33b9c34babaa7f4c861fa172935eaabea740), closes [#1084](https://github.com/driftyco/ionic/issues/1084))
|
||||
* **$ionicModal:**
|
||||
* pass modal instance to modal.shown/modal.hidden events ([a19e3b62](https://github.com/driftyco/ionic/commit/a19e3b62f80f69cec9d478a095b4a59d0dca8742), closes [#1065](https://github.com/driftyco/ionic/issues/1065))
|
||||
* show/hide/remove return promises for animating ([39385008](https://github.com/driftyco/ionic/commit/393850086fbff30f5f04026e70f8143c9c21f06f))
|
||||
* **$ionicNavBarDelegate:**
|
||||
* showBackButton returns whether bar is shown ([933a555e](https://github.com/driftyco/ionic/commit/933a555e084234462ad04b7255bd1a0e0c6b2aef), closes [#1076](https://github.com/driftyco/ionic/issues/1076))
|
||||
* showBar returns whether navbar is shown ([24a415c3](https://github.com/driftyco/ionic/commit/24a415c32dfe922174dd3a36e9b63bd2ea426fd9))
|
||||
* **$ionicScrollDelegate:**
|
||||
* add getScrollView(), getScrollPosition() ([b5ef9313](https://github.com/driftyco/ionic/commit/b5ef9313cfd227c716a2096a5ca40d38179ba50f), closes [#1117](https://github.com/driftyco/ionic/issues/1117))
|
||||
* add scrollBy(left,top,animate) to delegate ([f847c208](https://github.com/driftyco/ionic/commit/f847c2084774f51731239def71bd2612af5fc141), closes [#987](https://github.com/driftyco/ionic/issues/987))
|
||||
* **$ionicSideMenuDelegate:**
|
||||
* add isOpen() method ([518e54ee](https://github.com/driftyco/ionic/commit/518e54ee86aa816d57ff0dcff33d2ce75fa0e712), closes [#1074](https://github.com/driftyco/ionic/issues/1074))
|
||||
* add getOpenRatio() method ([ac0e981f](https://github.com/driftyco/ionic/commit/ac0e981f9e0c90458e7ab4bb3c86545a26320065), closes [#944](https://github.com/driftyco/ionic/issues/944))
|
||||
* **avatar:** Adding .item-avatar-left and deprecating .item-avatar ([9bac6050](https://github.com/driftyco/ionic/commit/9bac6050a0c4d7a3693eb9bcf8b7b7fc5cca980e))
|
||||
* **collection-repeat:** add repeat lsdirective for huge lists ([f0a1c037](https://github.com/driftyco/ionic/commit/f0a1c0375979ef959bfe1acf80f6ecebb473a8c6))
|
||||
* **grid:** Added .row-baseline and .row-stretch ([e0e7a83b](https://github.com/driftyco/ionic/commit/e0e7a83b076cc5c098c2b76348546bd716e5fddd))
|
||||
* **ionCheckbox:** allow ng-disabled attribute ([d2e54a82](https://github.com/driftyco/ionic/commit/d2e54a823a105dbf65a1800cfa8fff1b4adec82b), closes [#939](https://github.com/driftyco/ionic/issues/939))
|
||||
* **ionContent:** don't wrap in a .scroll element if scroll="false" ([73da93d4](https://github.com/driftyco/ionic/commit/73da93d4a42dd82ffbba9e18e12e70d39ff51a39), closes [#841](https://github.com/driftyco/ionic/issues/841))
|
||||
* **ionNavBar:** make back button animated ([97257938](https://github.com/driftyco/ionic/commit/972579383a73af991924639299c933bd50ba0f6e), closes [#1030](https://github.com/driftyco/ionic/issues/1030))
|
||||
* **ionSlideBox:** add pager-click attribute ([d6c960c2](https://github.com/driftyco/ionic/commit/d6c960c2a64372d158e675d6627209ae82d8c7ee), closes [#785](https://github.com/driftyco/ionic/issues/785))
|
||||
* **loading:** Moved loading scss to its own file and added variables, closes #984 ([e3491864](https://github.com/driftyco/ionic/commit/e3491864b2ae117cb35400891253ead917b298de))
|
||||
* **select:** Styled select elements, both inline and as a list item ([8a12f2d1](https://github.com/driftyco/ionic/commit/8a12f2d16c591199400ecbb478d5b047d6b46284))
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* $ionicPopup.show()'s button onTap function has changed.
|
||||
|
||||
When using `$ionicPopup.show()`, previously a button's onTap function
|
||||
would only result in closing the popup and resolving the promise if the
|
||||
`onTap(event)` function returned a truthy value.
|
||||
|
||||
Now, a button's onTap event will *always* close the popup and resolve
|
||||
the popup's promise, no matter the return value, by default. The only
|
||||
way to prevent the popup from closing is to call
|
||||
`event.preventDefault()`.
|
||||
|
||||
Change your code from this:
|
||||
|
||||
```js
|
||||
$ionicPopup.show({
|
||||
buttons: [{
|
||||
onTap: function(event) {
|
||||
if (!shouldClosePopup) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```js
|
||||
$ionicPopup.show({
|
||||
buttons: [{
|
||||
onTap: function(event) {
|
||||
if (!shouldClosePopup) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
```
|
||||
([cb1a5f62](https://github.com/driftyco/ionic/commit/cb1a5f62285fe5939274bdda9db169af69eddf35))
|
||||
* The developer should be stating exactly how an icon
|
||||
should show, but previously the right nav arrow icon violates this by
|
||||
automatically showing a right arrow when an item was an anchor or
|
||||
button. Instead of using the `:after` item selector, which was always
|
||||
applied by default, it uses the same markup as `item-icon-right`, which
|
||||
is easier to understand, customizable and not a hard coded default.
|
||||
|
||||
This change removes the `:after` nav icon styling, and creates a new
|
||||
class, `icon-accessory`, based off of similar CSS. The change makes a
|
||||
nav arrow highly customizable, allows RTL developers to easily control
|
||||
the arrow direction, and the accessory class is something that's
|
||||
reusable.
|
||||
|
||||
An example of right side arrow using `ion-chevron-right` as the icon:
|
||||
|
||||
<a class="item item-icon-right" href="#">
|
||||
Check mail
|
||||
<i class="icon ion-chevron-right icon-accessory"></i>
|
||||
</a>
|
||||
([c7e3defc](https://github.com/driftyco/ionic/commit/c7e3defca51f03368be84e7f86d71e7ec2fb374c))
|
||||
* ionic.Platform.isCordova() has been renamed to
|
||||
ionic.Platform.isWebView()
|
||||
([5c300dd3](https://github.com/driftyco/ionic/commit/5c300dd3e094e20ca90a311a704dfa0864b320f4))
|
||||
* ion-list syntax has changed in favor of simplicity &
|
||||
flexibility.
|
||||
|
||||
Relevant documentation:
|
||||
[ionList](http://ionicframework.com/docs/api/directive/ionList),
|
||||
[ionItem](http://ionicframework.com/docs/api/directive/ionItem),
|
||||
[ionOptionButton](http://ionicframework.com/docs/api/directive/ionOptionButton),
|
||||
[ionReorderButton](http://ionicframework.com/docs/api/directive/ionReorderButton),
|
||||
[ionDeleteButton](http://ionicframework.com/docs/api/directive/ionDeleteButton),
|
||||
[$ionicListDelegate](http://ionicframework.com/docs/api/service/$ionicListDelegate).
|
||||
|
||||
To migrate, change your code from this:
|
||||
|
||||
```html
|
||||
<ion-list option-buttons="[{text:'hello',type:'button-positive',onTap:tap()}]"
|
||||
on-delete="onDelete(el)"
|
||||
delete-icon="ion-minus-circled"
|
||||
can-delete="true"
|
||||
show-delete="shouldShowDelete"
|
||||
on-reorder="onReorder(el, startIndex, toIndex)"
|
||||
reorder-icon="ion-navicon"
|
||||
can-reorder="true"
|
||||
show-reorder="shouldShowReorder">
|
||||
<ion-item ng-repeat="item in items">
|
||||
{{item}}
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```html
|
||||
<ion-list show-delete="shouldShowDelete"
|
||||
show-reorder="shouldShowReorder">
|
||||
<ion-item ng-repeat="item in items">
|
||||
{{item}}
|
||||
<ion-delete-button class="ion-minus-circled"
|
||||
ng-click="onDelete(item)">
|
||||
</ion-delete-button>
|
||||
<ion-reorder-button class="ion-navicon"
|
||||
ng-click="onReorder(item, $fromIndex, $toIndex)">
|
||||
</ion-reorder-button>
|
||||
<ion-option-button class="button-positive" ng-click="tap()">
|
||||
Hello
|
||||
</ion-option-button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
```
|
||||
([986dbac8](https://github.com/driftyco/ionic/commit/986dbac8936f7472c1fe7237c02789a5a37dce65))
|
||||
* Before, if you did not have a `title` attribute set on your
|
||||
ion-view, it would transition into that view and erase the navbar's current
|
||||
title.
|
||||
|
||||
Now, if your ion-view does not have a `title` attribute set, the new
|
||||
view will be transitioned in, but there will be no title change.
|
||||
|
||||
If you wish to have a blank title on your new view, you must now
|
||||
explicitly set your `ion-view`'s title attribute to an empty string.
|
||||
|
||||
To migrate your code, change from this:
|
||||
|
||||
```html
|
||||
<ion-view></ion-view>
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```html
|
||||
<ion-view title=""></ion-view>
|
||||
```
|
||||
([d53eab81](https://github.com/driftyco/ionic/commit/d53eab819728e56ec3ee9a0906b79e277c30c07e))
|
||||
|
||||
|
||||
|
||||
<a name="1.0.0-beta.1"></a>
|
||||
### 1.0.0-beta.1 (2014-03-25)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **active:** Updated which elements to set active class along w/ tests, closes #857 ([423f9e4f](git://github.com/driftyco/ionic.git/commit/423f9e4f77288e5092347fcb5ab038225fec34bc))
|
||||
* **badge:** Badge horizontal alignment over item right side buttons, closes #826 ([ded46931](git://github.com/driftyco/ionic.git/commit/ded469311d7a0901fec79b98a87823b74c55b987))
|
||||
* **click:**
|
||||
* Increase the ghost click prevent delay for grade C devices ([001bcca4](git://github.com/driftyco/ionic.git/commit/001bcca4fa74c2ec4f51891566c0220a2bace23f))
|
||||
* enter key submission blocked. ([72ee799c](git://github.com/driftyco/ionic.git/commit/72ee799c4e26c916bd330e2fdd093a297a928229), closes [#819](git://github.com/driftyco/ionic.git/issues/819))
|
||||
* Click Events In SlideBox Fire Multiple Times, closes #808 ([f8a71377](git://github.com/driftyco/ionic.git/commit/f8a713774459aedeb5662febab759a4a81cb88fc))
|
||||
* **domready:** Fixed if firing off callbacks when DOM was already ready ([a637fb4d](git://github.com/driftyco/ionic.git/commit/a637fb4d1b0d64e3001d80576ab82484ee90340f))
|
||||
* **grid:** Correct responsive grid breaks for col-XX, closes #803 ([8fae85e9](git://github.com/driftyco/ionic.git/commit/8fae85e9e60232a6dad2c1b65cfeb6b9b495dfc8))
|
||||
* **input:** Fix input label from shifting when text is too long on iOS, closes #801 ([b8d4c51f](git://github.com/driftyco/ionic.git/commit/b8d4c51fe743759f216e5924d652636f86c2b55e))
|
||||
* **ionList:** only stop side menu drag if canSwipe ([c653e83c](git://github.com/driftyco/ionic.git/commit/c653e83cec69535d95dc8ae40af519e5e7f29320), closes [#709](git://github.com/driftyco/ionic.git/issues/709))
|
||||
* **ionTab:** stop browser-tooltip from appearing due to `title` attr ([aa30faf8](git://github.com/driftyco/ionic.git/commit/aa30faf86333f8e2065d08946b05ce4529b1f1ce), closes [#804](git://github.com/driftyco/ionic.git/issues/804))
|
||||
* **item:**
|
||||
* Restructure item editing css for added reusability and organization ([07c824db](git://github.com/driftyco/ionic.git/commit/07c824db8d465a256c642dcd91d988688f6551f1))
|
||||
* fix avatar/thumbnail in .item-complex, and avatar misspelling ([947b8d69](git://github.com/driftyco/ionic.git/commit/947b8d6943376b4dd0c181cf4390650289f76d4a))
|
||||
* **listView:**
|
||||
* send index on delete. ([75107771](git://github.com/driftyco/ionic.git/commit/75107771566ac4467c45497f657c0131ad3b2941), closes [#849](git://github.com/driftyco/ionic.git/issues/849))
|
||||
* only allow one swipeable item open. ([73b750fb](git://github.com/driftyco/ionic.git/commit/73b750fb37c26518fd9b34959b77012430e6ad45), closes [#763](git://github.com/driftyco/ionic.git/issues/763))
|
||||
* No slide drag if no hidden buttons. ([4e605979](git://github.com/driftyco/ionic.git/commit/4e605979ec8d80443d5a0722ea6ebc7227a4e2f3), closes [#847](git://github.com/driftyco/ionic.git/issues/847))
|
||||
* **pointer:** Add pointer styling to .item[ng-click], closes #858 ([aa280910](git://github.com/driftyco/ionic.git/commit/aa280910dfc1d9e798f9fd0b5401ee59730d6ee8))
|
||||
* **popup:**
|
||||
* Ensure popup is usable on top of a modal, closes #838 ([6ebfe776](git://github.com/driftyco/ionic.git/commit/6ebfe776bcca506e2168756b69e4823199c70c43))
|
||||
* focus popup. ([dddc34d8](git://github.com/driftyco/ionic.git/commit/dddc34d8d24273aecf15261dbc2f9df1b0f068d0), closes [#820](git://github.com/driftyco/ionic.git/issues/820))
|
||||
* **range:** Clicking Line For Range Causes Drag Button To Follow Mouse, close #779 ([26c8f304](git://github.com/driftyco/ionic.git/commit/26c8f304e7d9d24006d17fcb4161195717ca26a0))
|
||||
* **reorder:** Prevent scroll w/ data-prevent-scroll attr on reorder btn, closes #848 ([f1ed4b00](git://github.com/driftyco/ionic.git/commit/f1ed4b0084d6457959fe4c65cdefac34148278c3))
|
||||
* **scrollView:**
|
||||
* browserify issue: undefined core ([cd27e1b7](git://github.com/driftyco/ionic.git/commit/cd27e1b79289c1ea6c8fea78da56d0c0c56dc827), closes [#825](git://github.com/driftyco/ionic.git/issues/825))
|
||||
* pull to refresh spin back. ([049aabc7](git://github.com/driftyco/ionic.git/commit/049aabc706cd4aae6b5608db2c7ba0f12b75b75a), closes [#774](git://github.com/driftyco/ionic.git/issues/774))
|
||||
* show bar with mouse wheel. ([d5a69575](git://github.com/driftyco/ionic.git/commit/d5a695757ace70f40db986d7793ecc72c2b4f2d1), closes [#809](git://github.com/driftyco/ionic.git/issues/809))
|
||||
* don't show bars if not scrolling. ([cb686636](git://github.com/driftyco/ionic.git/commit/cb686636cd34da6880036f7d72c8de18ed70d9b2), closes [#805](git://github.com/driftyco/ionic.git/issues/805))
|
||||
* **tabs:** Renamed .tab-item active state from .active to .tab-item-active, closes #866 ([24160aa0](git://github.com/driftyco/ionic.git/commit/24160aa0bdd2df262d27fd14bc99c6d0e434eac0))
|
||||
* **tap:**
|
||||
* Trigger clicks if touch/click held for more than 250ms, closes #791 ([60e45333](git://github.com/driftyco/ionic.git/commit/60e453331379c296534dc0fa43bf229016759a7c))
|
||||
* Reset startCoordinates on touchend/mouseup, closes #874 ([76a53134](git://github.com/driftyco/ionic.git/commit/76a531348078eb9a523e9123cadca9e3500f837d))
|
||||
* Remove select element from tap checking, closes #836 ([3d917c83](git://github.com/driftyco/ionic.git/commit/3d917c83f57bf7c568c08393472cbc7863322499))
|
||||
* **toggle:** Changed tap listener to use "release", closes #882 #881 ([b1a7c199](git://github.com/driftyco/ionic.git/commit/b1a7c1990ad95d53bd10e7dd7215018671413c3e))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$ionicLoading:** implement backdrop class ([57d71ed6](git://github.com/driftyco/ionic.git/commit/57d71ed6c321921a0b1adf501a2ce0bf432f3f61), closes [#837](git://github.com/driftyco/ionic.git/issues/837))
|
||||
* **$ionicScrollDelegate:** rememberScrollPosition, scrollToRememberedPosition ([5a0efece](git://github.com/driftyco/ionic.git/commit/5a0efecef6ea2c4f89c6dfd3cbe98a98614a6e17))
|
||||
* **content:** automatically add/remove has-* classes to content ([e94d4006](git://github.com/driftyco/ionic.git/commit/e94d400648a0770cea3909e2d2513c71e88978ae), closes [#619](git://github.com/driftyco/ionic.git/issues/619))
|
||||
* **grid:** Remove column offset with responsive grid breaks ([73ba2a40](git://github.com/driftyco/ionic.git/commit/73ba2a405fabb306ac85f039620ee5df2d12bff2))
|
||||
* **ion-content:** watch padding attribute ([532d473e](git://github.com/driftyco/ionic.git/commit/532d473e351db95160c3e8a2878dbf5a1de549a9))
|
||||
* **ionTabs:** add available tabs-item-hide class ([5966dbf4](git://github.com/driftyco/ionic.git/commit/5966dbf43e56f2f65b18cb6f030a7b60ead236f2), closes [#395](git://github.com/driftyco/ionic.git/issues/395))
|
||||
* **ionic:** remove all delegates ([dbe4e390](git://github.com/driftyco/ionic.git/commit/dbe4e3901d6ee70bae85e48b6e58c097b9f2810e))
|
||||
* **ionicNavBar:** add getTitle() and getPreviousTitle() methods ([215b1c1e](git://github.com/driftyco/ionic.git/commit/215b1c1ea058bb76e4950d06e3e7e127c5a43cc6))
|
||||
* **loadingView:**
|
||||
* add setContent method ([366bd686](git://github.com/driftyco/ionic.git/commit/366bd6866ffa513c1d99b36b841d5ad3fbe23622), closes [#732](git://github.com/driftyco/ionic.git/issues/732))
|
||||
* add setContent method ([e5cba05e](git://github.com/driftyco/ionic.git/commit/e5cba05e90a0c1f7ce6b032020ef212b19b7bc84))
|
||||
* **navclear:** Ability to disable the next view transition and back button ([f744d9eb](git://github.com/driftyco/ionic.git/commit/f744d9ebcfaad9be237fd2b1753568bf832bfe0a))
|
||||
* **popup:** Support for programatically closing popup. ([dc2b24ed](git://github.com/driftyco/ionic.git/commit/dc2b24ed6aa14f7db6c70791c16c5afc5e909c66), closes [#854](git://github.com/driftyco/ionic.git/issues/854))
|
||||
* **progress:** Set progress element's default width to 100%, closes #872 ([b9cde47d](git://github.com/driftyco/ionic.git/commit/b9cde47dd039bc58f84e0af76fccbfcc9d7be74e))
|
||||
* **ready:** Add 'platform-ready' css class to the body when the platform is ready ([681a6a2e](git://github.com/driftyco/ionic.git/commit/681a6a2ed743fab2352551156ff18dbb42549d4d))
|
||||
* **sideMenu:** Added directive for simple toggling ([5a89df43](git://github.com/driftyco/ionic.git/commit/5a89df43363b1ea88bff25c73f019462c964fa7d))
|
||||
|
||||
#### Breaking Changes / Migration Guide
|
||||
|
||||
* **ionTabs**: `tabs-type` and `tabs-style` removed. Use classNames instead.
|
||||
|
||||
Relevant Documentation: [ionTabs](http://ionicframework.com/docs/api/directive/ionTabs).
|
||||
|
||||
Old Code:
|
||||
|
||||
`<ion-tabs tabs-type="tabs-top" tabs-style="tabs-positive" animation="slide-left-right">`
|
||||
|
||||
New Code:
|
||||
|
||||
`<ion-tabs class="tabs-top tabs-positive slide-left-right">`
|
||||
|
||||
* **ionHeaderBar, ionFooterBar**: remove `type`, `title`, `left-buttons`, `right-buttons`.
|
||||
|
||||
Relevant Documentation: [ionHeaderBar](http://ionicframework.com/docs/api/directive/ionHeaerBar),
|
||||
[ionFooterBar](http://ionicframework.com/docs/api/directive/ionFooterBar).
|
||||
|
||||
Old Code:
|
||||
|
||||
```html
|
||||
<ion-header-bar type="bar-positive"
|
||||
title="{{someTitle}}"
|
||||
left-buttons="left"
|
||||
right-buttons="right">
|
||||
</ion-header-bar>
|
||||
```
|
||||
|
||||
New Code:
|
||||
|
||||
```html
|
||||
<ion-header-bar class="bar-positive">
|
||||
<div class="buttons">
|
||||
<button class="button" ng-click="leftButtonTap()">
|
||||
Left Button
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="title">{{someTitle}}</h>
|
||||
<div class="buttons">
|
||||
<button class="button" ng-click="rightButtonTap()">
|
||||
Right Button
|
||||
</button>
|
||||
</div>
|
||||
</ion-header-bar>
|
||||
```
|
||||
|
||||
* **ionNavBar, ionView**: Remove `left-buttons`, `right-buttons`, `type`, `animation`,
|
||||
`back-button-*` attributes from ionNavBar. Remove `left-buttons`, `right-buttons` from ionView.
|
||||
|
||||
Additionally, all 'viewState.*' events have been removed. Use [$ionicNavBarDelegate](http://ionicframework.com/docs/api/service/$ionicNavBarDelegate).
|
||||
|
||||
Relevant Documentation: [ionNavBar](http://ionicframework.com/docs/api/directive/ionNavBar),
|
||||
[ionView](http://ionicframework.com/docs/api/directive/ionView),
|
||||
[ionNavBackButton](http://ionicframework.com/docs/api/directive/ionNavBackButton) (new),
|
||||
[ionNavButtons](http://ionicframework.com/docs/api/directive/ionNavButtons) (new).
|
||||
|
||||
Old Code:
|
||||
|
||||
```html
|
||||
<ion-nav-bar type="bar-positive"
|
||||
animation="slide-left-right"
|
||||
back-button-type="button-icon"
|
||||
back-button-icon="ion-arrow-left-c"
|
||||
back-button-label="Back">
|
||||
</ion-nav-bar>
|
||||
<ion-nav-view>
|
||||
<ion-view left-buttons="leftButtons"
|
||||
title="someTitle"
|
||||
right-buttons="rightButtons">
|
||||
</ion-view>
|
||||
</ion-nav-view>
|
||||
```
|
||||
|
||||
New Code:
|
||||
|
||||
```html
|
||||
<ion-nav-bar class="bar-positive slide-left-right">
|
||||
<ion-nav-back-button class="button-icon ion-arrow-left-c">
|
||||
Back
|
||||
</ion-nav-back-button>
|
||||
</ion-nav-bar>
|
||||
<ion-nav-view>
|
||||
<ion-view title="someTitle">
|
||||
|
||||
<ion-nav-buttons side="left">
|
||||
<button class="button" ng-click="leftButtonTap()">
|
||||
Left Button
|
||||
</button>
|
||||
</ion-nav-buttons>
|
||||
<ion-nav-buttons side="right">
|
||||
<button class="button" ng-click="rightButtonTap()">
|
||||
Right Button
|
||||
</button>
|
||||
</ion-nav-buttons>
|
||||
|
||||
</ion-view>
|
||||
</ion-nav-view>
|
||||
```
|
||||
|
||||
* **ionSideMenuContent**: is now an element directive.
|
||||
|
||||
Relevant documentation: [ionSideMenus](http://ionicframework.com/docs/api/directive/ionSideMenus),
|
||||
[ionSideMenuContent](http://ionicframework.com/docs/api/directive/ionSideMenuContent),
|
||||
[ionSideMenu](http://ionicframework.com/docs/api/directive/ionSideMenu).
|
||||
|
||||
Old Code:
|
||||
|
||||
```html
|
||||
<ion-side-menus>
|
||||
<ion-pane ion-side-menu-content>
|
||||
</ion-pane>
|
||||
</ion-side-menus>
|
||||
```
|
||||
|
||||
New Code:
|
||||
|
||||
```html
|
||||
<ion-side-menus>
|
||||
<ion-side-menu-content>
|
||||
</ion-side-menu-content>
|
||||
</ion-side-menus>
|
||||
```
|
||||
|
||||
|
||||
<a name="0.9.27"></a>
|
||||
### 0.9.27 "Salamander" (2014-03-15)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **actionsheet:**
|
||||
* Use transition instead of animation for backdrop, fix for Android 2.3 ([c91622b7](https://github.com/driftyco/ionic/commit/c91622b760dd4e919f890de2ecdf46d4e9573d13))
|
||||
* Dismiss actionsheet when tapping backdrop, closes #733 ([668c646f](https://github.com/driftyco/ionic/commit/668c646faa25a10d67cf2beebb4bf119e758ff1d))
|
||||
* Fix slide-up animation, closes #713 ([44b31e18](https://github.com/driftyco/ionic/commit/44b31e18878b89d47d769505a619aa7f97cf740a))
|
||||
* **backbutton:** Allow only one back button listener to run per click, closes #693 ([a491f22c](https://github.com/driftyco/ionic/commit/a491f22c1f2297dcbe7dbd9679593b896584cc87))
|
||||
* **badge:**
|
||||
* Vertical align a badge in an item-divider, closes #707 ([cb5510c1](https://github.com/driftyco/ionic/commit/cb5510c1843c3debeb0b29d71cda7abbcd00f753))
|
||||
* Update badge alignment in tab items, closes #694 ([c4eed02f](https://github.com/driftyco/ionic/commit/c4eed02f29be9a82752915ee32c12be4f453645a))
|
||||
* **button:**
|
||||
* Correct vertical alignment of header right buttons, closes #704 ([438f5fba](https://github.com/driftyco/ionic/commit/438f5fba582b7e391ec174d4c9a37d53579b7b47))
|
||||
* Tweak vertical alignment of button icons, closes #739 ([7ec0605e](https://github.com/driftyco/ionic/commit/7ec0605e06f89c6f3008c7ff16c795f6535822dc))
|
||||
* **checkbox:** Fix checkmark in Android 2.3 ([717148d9](https://github.com/driftyco/ionic/commit/717148d9868922d899c29403d97943b679749617))
|
||||
* **colors:** Update all #4A87EE colors to use $positive color, closes #731 ([d113ddfa](https://github.com/driftyco/ionic/commit/d113ddfa52c2c751be78de0770b50da0c7407123))
|
||||
* **footer:** Show footers within tab content, closes #728 ([9c5772f3](https://github.com/driftyco/ionic/commit/9c5772f3c3aeb41ea10d8484867f4f240f48d953))
|
||||
* **history:** Separate histories and views, clear other views in clearHistory(), closes #724 ([c99427aa](https://github.com/driftyco/ionic/commit/c99427aa9221caa9297a204abc7558c33594c38a))
|
||||
* **input:** Correct vertical alignment of inputs and their labels, closes #799 ([6547ca60](https://github.com/driftyco/ionic/commit/6547ca60cbf445cb225f74154d4fa1563388229c))
|
||||
* **ionTabs:** do not pre-transclude; stops error on compile ([ecfdbaa6](https://github.com/driftyco/ionic/commit/ecfdbaa67b076bab61e9573fc650ee6eccb459b1), closes [#730](https://github.com/driftyco/ionic/issues/730))
|
||||
* **item:**
|
||||
* Fix badge moving to new line when text is too long, closes #551 ([4d366710](https://github.com/driftyco/ionic/commit/4d3667106e4078dc411aa55244b5efbd1e5672df))
|
||||
* Fix item-icon-left / right animating Ionicon not centered, closes #670 ([11a4338d](https://github.com/driftyco/ionic/commit/11a4338d136bb5be26f58a794b8c2e1ad9fe429f))
|
||||
* **list:** Drag to expose list option buttons, closes #701 ([25650005](https://github.com/driftyco/ionic/commit/2565000564b12f408ac025d1a6b6dea2fe8c1181))
|
||||
* **modal:**
|
||||
* Do not apply the same modal animation to all of its children, closes #683 ([9cff5d03](https://github.com/driftyco/ionic/commit/9cff5d030ce4b25abd0cfe1c68425b82761063d4))
|
||||
* Fix removing modal from DOM on .remove(), closes #755 ([c4ca7a85](https://github.com/driftyco/ionic/commit/c4ca7a85e0014cadd047065522b13b6eae4788f8))
|
||||
* Improve slide-up/down animations, remove flickers ([b593cf1b](https://github.com/driftyco/ionic/commit/b593cf1be67a98e95ba0d71c0997da0a9bda4bf7))
|
||||
* **navView:** _getView renamed to _getViewById, closes #736 ([78206d0e](https://github.com/driftyco/ionic/commit/78206d0e7caee2212b13b75044681cdd534e9822))
|
||||
* **platform:** Fix Platform.showStatusBar so it can be used multiple times, closes #702 ([a6c47cd3](https://github.com/driftyco/ionic/commit/a6c47cd3bfb5d441d906f18d85e39d10d90326ec))
|
||||
* **sideMenu:**
|
||||
* Not snapping on close on certain conditions #795 ([a5899918](https://github.com/driftyco/ionic/commit/a58999180b9dbf4d07823dfef9cc67b8babc25ac))
|
||||
* Fix flashing when closing right side menu, closes #556 ([a0d60d52](https://github.com/driftyco/ionic/commit/a0d60d5228e385b952d6561ed6454e6add45401e))
|
||||
* Close side-menu if open and content tapped, closes #648 ([0c5c6751](https://github.com/driftyco/ionic/commit/0c5c675164eaf785f050e517842aa2002559c97f))
|
||||
* **sideMenuController:** sticking issue #738 ([ea04e393](https://github.com/driftyco/ionic/commit/ea04e393c00b629455e737b64bdaccff6ff99424))
|
||||
* **sidemenu:** Side menu always needs to have translate3d applied, closes #710 ([16ac2ff1](https://github.com/driftyco/ionic/commit/16ac2ff1d6ec7f7a32e8b6f7a21c3e7a807dbe4d))
|
||||
* **tap:**
|
||||
* Do not trigger a click if the element was scrolled after touchstart/mousedown ([98e7e1aa](https://github.com/driftyco/ionic/commit/98e7e1aa87ef6fddc01443faa447a7f72f56994d))
|
||||
* Do not detect taps for input[type=file], closes #652 ([6f2d6e7c](https://github.com/driftyco/ionic/commit/6f2d6e7c5a775218a0f7424e6bdb0269fd9c7603))
|
||||
* **toggle:** Fix toggle handle on Android 2.3 ([72f2e840](https://github.com/driftyco/ionic/commit/72f2e8407eabc87329ea89e476e9fd9b9989d405))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **active:** Removing use of :active in favor of .active for more control of active state ([baa04cde](https://github.com/driftyco/ionic/commit/baa04cde4d7292d07ec2bfae949e008aeaafae1b))
|
||||
* **animation:** Add right to left animations, and their reverse, for RTL support, closes #643 ([4628b9fb](https://github.com/driftyco/ionic/commit/4628b9fb23c16cab8ba969342f6bd0be1a1bfc84))
|
||||
* **ionInfiniteScroll:**
|
||||
* allow configuration of icon and text through `icon` and `text` attributes ([5f2c32ea](https://github.com/driftyco/ionic/commit/5f2c32ea9b19a773e943a9b21704f2138037e681))
|
||||
* **ionRefresher:** allow custom text & icons through `pulling-text`, `pulling-icon`, `refreshing-text`, `refreshing-icon` attributes. ([573df56d](https://github.com/driftyco/ionic/commit/573df56db4d79eee517df61b45c4f780a58ce4f8), closes [#760](https://github.com/driftyco/ionic/issues/760))
|
||||
* **modal:**
|
||||
* On larger displays modals will be inset and centered, not full width/height, clo ([ba2a40c8](https://github.com/driftyco/ionic/commit/ba2a40c845dade7cdc6f99de14b5467e327d1d3c))
|
||||
* Create a modal backdrop wrapper w/ internal modal directive, closes #605 ([7d076bd5](https://github.com/driftyco/ionic/commit/7d076bd55b2117b3ac569ef41ae9f42bc56eca58))
|
||||
* **popup:** Added popup support ([a30b0b7d](https://github.com/driftyco/ionic/commit/a30b0b7d4fa5bc83e0ccdadee1d78faf996bef00))
|
||||
* **toggle:** Added dragging support to toggle switches ([cc15a5b4](https://github.com/driftyco/ionic/commit/cc15a5b455b7b59469eb04503f0a7292d4ceddf2))
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* on-refresh and on-refresh-opening are no longer on the
|
||||
ion-content directive. They are on the ion-refresher. In addition,
|
||||
on-refresh-opening has been renamed to on-pulling.
|
||||
|
||||
Change your code from this:
|
||||
|
||||
```html
|
||||
<ion-content on-refresh="onRefresh()"
|
||||
on-refresh-opening="onRefreshOpening()">
|
||||
<ion-refresher></ion-refresher>
|
||||
</ion-content>
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-refresher on-refresh="onRefresh()"
|
||||
on-pulling="onRefreshOpening()">
|
||||
</ion-refresher>
|
||||
</ion-content>
|
||||
```
|
||||
([573df56d](https://github.com/driftyco/ionic/commit/573df56db4d79eee517df61b45c4f780a58ce4f8))
|
||||
|
||||
* on-infinite-scroll and infinite-scroll-distance are no longer attributes on the ion-content directive. They are on the ion-infinite-scroll element.
|
||||
|
||||
Chang your code from this:
|
||||
|
||||
```html
|
||||
<ion-content on-infinite-scroll="onInfiniteScroll()"
|
||||
infinite-scroll-distance="1%">
|
||||
</ion-content>
|
||||
```
|
||||
|
||||
To this:
|
||||
|
||||
```html
|
||||
<ion-content>
|
||||
<ion-infinite-scroll on-infinite="onInfiniteScroll()"
|
||||
distance="1%"
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
```
|
||||
|
||||
|
||||
*
|
||||
ionHeaderBar's title attribute is now interpolated.
|
||||
|
||||
Change this code: `<ion-header-bar title="myTitleVar"></ion-header-bar>`
|
||||
|
||||
To this code: `<ion-header-bar title="{{myTitleVar}}"></ion-header-bar>`
|
||||
([a8e1524c](https://github.com/driftyco/ionic/commit/a8e1524ce8e6d2a805770585ffd7bb457460a104))
|
||||
*
|
||||
ionicSlideBox#getPos has been renamed to ionicSlideBox#currentIndex.
|
||||
|
||||
ionicSlideBox#numSlides has been renamed to ionicSlideBox#slidesCount.
|
||||
([1dd55276](https://github.com/driftyco/ionic/commit/1dd552765568ba272dcc132a4889140c259b3ff1))
|
||||
|
||||
|
||||
<a name="0.9.26"></a>
|
||||
### 0.9.26 "Rabbit" (2014-02-26)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **actionsheet:** Actionsheet in modal has pointer-events enabled, closes #660 ([1503cc72](http://github.com/driftyco/ionic/commit/1503cc7213c13e87804c210dea7465931e972f4a))
|
||||
* **click:** event.preventDefault() when setting focus() on an input, closes 583 ([fc8ab4b8](http://github.com/driftyco/ionic/commit/fc8ab4b8ea9b89bd3446b835476950bb70bba879))
|
||||
* **ionContent:**
|
||||
* Update scss for ion-infinite-scroll element ([788df524](http://github.com/driftyco/ionic/commit/788df5243dc5300204171bcd40bf1e20fe610dbe))
|
||||
* Use new name to find ion-infinite-scroll element ([1702f5c9](http://github.com/driftyco/ionic/commit/1702f5c916dda20957761fd1e7b30d5f9bc1b6b4))
|
||||
* **ionPrefix:** disableRegisterByTagName updated w/ `ion-` prefix for correct view history ([2494b5f9](http://github.com/driftyco/ionic/commit/2494b5f988991b97c278102f0da385e9cde00aee))
|
||||
* **ionSideMenu:** use manual transclude instead of ngTransclude ([991d3cfd](http://github.com/driftyco/ionic/commit/991d3cfda1831467e38c176c69f0ecd084c86e50), closes [#666](http://github.com/driftyco/ionic/issues/666))
|
||||
* **ionTabs:** cleanup and fix many issues ([0f1b6f47](http://github.com/driftyco/ionic/commit/0f1b6f47b8ee4c72facc269da2b648bb9cc397e0), closes [#597](http://github.com/driftyco/ionic/issues/597))
|
||||
* **ionicScrollDelegate:**
|
||||
* do not error if no scrollTop/Left values ([9e942f89](http://github.com/driftyco/ionic/commit/9e942f894b037284a2e36ff14d3615f076b84447), closes [#659](http://github.com/driftyco/ionic/issues/659)
|
||||
* **item:** degrade .item right arrows by grade for low end devices ([3a69bb34](http://github.com/driftyco/ionic/commit/3a69bb3452944bc4d1764c7605aad0824cbb758c))
|
||||
* **loading:** make showDelay option work correctly ([7281e2ab](http://github.com/driftyco/ionic/commit/7281e2abf0f6f624f296191b3f322227089e3658), closes [#562](http://github.com/driftyco/ionic/issues/562))
|
||||
* **modal:**
|
||||
* Fix modal animation for firefox, closes #671 ([0033c880](http://github.com/driftyco/ionic/commit/0033c8809a810c8b518ca68a9ada5704a7f6e9c9))
|
||||
* **navBar:**
|
||||
* animations, hide back button, no flicker ([465ea769](http://github.com/driftyco/ionic/commit/465ea76969b348b3074747dcd4cebf0861a38e6d), closes [#653](http://github.com/driftyco/ionic/issues/653))
|
||||
* **platform:** Update ionic.Platform.is() to check all platforms, closes #604 ([fcd0fa73](http://github.com/driftyco/ionic/commit/fcd0fa73c4125593e6efce6b6410586f5a27fc02))
|
||||
* **sideMenu:** remove translate3d when not needed, close #636 ([07092f00](http://github.com/driftyco/ionic/commit/07092f009cfa2b52b2dc2fdb915b237bd8c37dfa))
|
||||
* **slideBox:** make `does-continue` attribute work continuously ([f6ec6a3c](http://github.com/driftyco/ionic/commit/f6ec6a3c89116174541c2fb08194a04ada92065b), closes [#575](http://github.com/driftyco/ionic/issues/575))
|
||||
* **tabs:**
|
||||
* Double tapping a tab would set the wrong view history, closes #656 ([f0faae16](http://github.com/driftyco/ionic/commit/f0faae16574b1247b6f5b1fa0180cfb9248804a1))
|
||||
* **text-rendering:** Disable text-rendering:optimizeLegibility for low end devices ([10289466](http://github.com/driftyco/ionic/commit/10289466162f4efa36dc902052b78e42d0df0b2d))
|
||||
* **thumbnail:** Correctly apply thumbnail style to item directive, closes #509 ([977c1cc6](http://github.com/driftyco/ionic/commit/977c1cc6e511d752e083081adbed9f8fda1d0312))
|
||||
* **transform:** Polyfill `style.transform` to work w/ non-webkit ([52671c18](http://github.com/driftyco/ionic/commit/52671c18f97d057aca581a566cc7ea27cc055314))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **grade:** Set grade in body class depending on platform performance ([b69b40c8](http://github.com/driftyco/ionic/commit/b69b40c82684acf3003f19277a44caf4614c75e4))
|
||||
* **ionContent:** use child scope instead of isolate scope ([49e0dac9](http://github.com/driftyco/ionic/commit/49e0dac9992d60ca098eb1dbd1acecbc74eae85c), closes [#555](http://github.com/driftyco/ionic/issues/555))
|
||||
* **ionInfiniteScroll:** use event system ([7b0716c2](http://github.com/driftyco/ionic/commit/7b0716c23c44fe5530526c86d590111382f991d7), closes [#661](http://github.com/driftyco/ionic/issues/661))
|
||||
* **ionNavAnimation:** `<a href="#/page" ion-nav-animation="slide-in-up">` ([8354d42b](http://github.com/driftyco/ionic/commit/8354d42b4b049e97bd93ce8ebacf047284a8fad1))
|
||||
* **item:** Auto right-arrow for complex list items w/ ng-click/href, closes #472 ([327a6866](http://github.com/driftyco/ionic/commit/327a686626c81072df8e055c8be220144fade26a))
|
||||
([110ff9f4](http://github.com/driftyco/ionic/commit/110ff9f47583c7f04bcf5b1eebea6d7bd0b25e99), closes [#243](http://github.com/driftyco/ionic/issues/243))
|
||||
* **sass:** All variables now have !default assigned, closes #631 ([53af2c7a](http://github.com/driftyco/ionic/commit/53af2c7a0177f9bcd2b43d9179832a319ecb8f7f))
|
||||
* **scrollbar:** Do not use rgba background for scrollbar on grade-b and c devices ([805c35c1](http://github.com/driftyco/ionic/commit/805c35c1f119530777498177016bb0c448e92b90))
|
||||
* **sideMenu:**
|
||||
* Degrade .menu-content box-shadow w/ platform grade for animation performance ([d2a0780b](http://github.com/driftyco/ionic/commit/d2a0780bbac5b17525def8b75c5c58c168675631))
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* The binding for ionInfiniteScroll has changed, as well
|
||||
as how you finish it.
|
||||
|
||||
If you had this code before:
|
||||
|
||||
```html
|
||||
<ion-content on-infinite-scroll="doSomething"></ion-content>
|
||||
```
|
||||
```js
|
||||
function MyCtrl($scope) {
|
||||
$scope.doSomething = function(scrollDoneCallback) {
|
||||
doSomething();
|
||||
scrollDoneCallback();
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Now, your code should look like this:
|
||||
|
||||
```html
|
||||
<ion-content on-infinite-scroll="doSomething()"></ion-content>
|
||||
```
|
||||
```js
|
||||
function MyCtrl($scope) {
|
||||
$scope.doSomething = function() {
|
||||
doSomething();
|
||||
$scope.$broadcast('scroll.infiniteScrollComplete');
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="0.9.25"></a>
|
||||
### 0.9.25 "Quokka" (2014-02-19)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **anchorScroll:** find offset of nested elements correctly ([17cc0408](http://github.com/driftyco/ionic/commit/17cc04089600bda36e5d3010635967fd7d008f96), closes [#618](http://github.com/driftyco/ionic/issues/618))
|
||||
* **click:** event.preventDefault() when setting focus() on an input, closes 583 ([fc8ab4b8](http://github.com/driftyco/ionic/commit/fc8ab4b8ea9b89bd3446b835476950bb70bba879))
|
||||
* **ionicLoading:** make showDelay default to 0 (was 2000) ([0d3718cc](http://github.com/driftyco/ionic/commit/0d3718cc218037e32f902da6793a7df9071a0c5a))
|
||||
* **ionicScrollDelegate:** tapScrollToTop won't fire for button tap ([70d95249](http://github.com/driftyco/ionic/commit/70d952499aedddb6516070d500769b800aec7b4f), closes [#557](http://github.com/driftyco/ionic/issues/557))
|
||||
* **loading:** make showDelay option work correctly ([7281e2ab](http://github.com/driftyco/ionic/commit/7281e2abf0f6f624f296191b3f322227089e3658), closes [#562](http://github.com/driftyco/ionic/issues/562))
|
||||
* **modal:** do not click buttons underneath modal ([9bc928f0](http://github.com/driftyco/ionic/commit/9bc928f031613879c42587cad5169b82d1621145))
|
||||
* **navBar:** animations work properly ([749cd382](http://github.com/driftyco/ionic/commit/749cd3829ccedacd552abfd4a2c607066f12c0b2))
|
||||
* **scrollView:**
|
||||
* allow contenteditable element to be pressed normally ([39ad3e0b](http://github.com/driftyco/ionic/commit/39ad3e0b2667eb036cbf146121aabf8383506a8c), closes [#421](http://github.com/driftyco/ionic/issues/421))
|
||||
* fix error from checking device before ready ([a5d96473](http://github.com/driftyco/ionic/commit/a5d964734f0f89ca506edbb38e0cdd7fc5469b90))
|
||||
* cancel scrollTop every time hash is set ([e1b6fd4f](http://github.com/driftyco/ionic/commit/e1b6fd4f8406257f1dd7eee4e114ab6fe119b4b5))
|
||||
* do not stop scrolling if stopped beyond boundaries ([1aef593f](http://github.com/driftyco/ionic/commit/1aef593f07d9651e1cdd00051584dfc76bf10076))
|
||||
* **tabs:** broadcast tab.shown/tab.hidden to only child scopes ([69fda4e5](http://github.com/driftyco/ionic/commit/69fda4e5267e8c66e3f3f232a10d160cc0ced338))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **angular:** Update to Angular v1.2.12, closes #600 ([97f4f6ea](http://github.com/driftyco/ionic/commit/97f4f6eacea512c5ef3845e0ba89663ef0758915))
|
||||
* **button:** Increase hit area size of a button ([c168b489](http://github.com/driftyco/ionic/commit/c168b489b5347f716e6463c7f9335dcc45fbc1b5))
|
||||
* **event:** Created stopEvent directive to use for certain ng-click cases, closes #550 ([8b308a17](http://github.com/driftyco/ionic/commit/8b308a1737e29670b88c1c9fe10d137d912edbcc))
|
||||
* **ionic:** prefix all directives with `ion-` ([2c39a214](http://github.com/driftyco/ionic/commit/2c39a214981b039602891c85028f7e87b9d67be1))
|
||||
* **modal:**
|
||||
* add .isShown() method to modal instances ([e106457e](http://github.com/driftyco/ionic/commit/e106457e61b5510a4a2e2a62a7015e8a2fb83313), closes [#320](http://github.com/driftyco/ionic/issues/320))
|
||||
* $broadcast 'modal.shown/hidden/removed' from parent scope ([110ff9f4](http://github.com/driftyco/ionic/commit/110ff9f47583c7f04bcf5b1eebea6d7bd0b25e99), closes [#243](http://github.com/driftyco/ionic/issues/243))
|
||||
* **navBar:** allow expression in `type`. `<nav-bar type="{{myType}}">` ([5470d77a](http://github.com/driftyco/ionic/commit/5470d77ac0b1812f13b162e3d7e38f8d16e5eaf1), closes [#599](http://github.com/driftyco/ionic/issues/599))
|
||||
* **sideMenu:** allow and watch attrs `width` & `is-enabled` ([bfefc69f](http://github.com/driftyco/ionic/commit/bfefc69f3c87cb51c918953def3ec92277e73edc))
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* All directives are now prefixed with `ion-`.
|
||||
|
||||
For any directive you use, add the ionic prefix.
|
||||
|
||||
For example, change this HTML:
|
||||
|
||||
```html
|
||||
<tabs>
|
||||
<tab title="home" href="/tab/home">
|
||||
<content>Hello!</content>
|
||||
</tab>
|
||||
</tabs>
|
||||
```
|
||||
|
||||
To this HTML:
|
||||
|
||||
```
|
||||
<ion-tabs>
|
||||
<ion-tab title="home" href="/tab/home">
|
||||
<ion-content>Hello!</ion-content>
|
||||
</ion-tab>
|
||||
</ion-tabs>
|
||||
```
|
||||
([2c39a214](http://github.com/driftyco/ionic/commit/2c39a214981b039602891c85028f7e87b9d67be1))
|
||||
|
||||
|
||||
<a name="0.9.24"></a>
|
||||
### 0.9.24 "Peleguin" (2014-02-12)
|
||||
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **android:** when keyboard comes up, ensure input is in view ([9327ac71](https://github.com/driftyco/ionic/commit/9327ac71c778fa7ad48eb5570687e9380f5ff0db), closes [#314](https://github.com/driftyco/ionic/issues/314))
|
||||
* **backButton:**
|
||||
* able to hide back button if any back button attr set in navBar, closes #564 ([74a05a03](https://github.com/driftyco/ionic/commit/74a05a03388f1a9a77141f078623b018bf2829eb))
|
||||
* Do not show back button if no attributes set, closes #549 ([2d39418d](https://github.com/driftyco/ionic/commit/2d39418d0b322e6e2ab2b054035e4c3abeabfaff))
|
||||
* **browser:** on first hash-set, dont set scrollTop ([1c4d4a8b](https://github.com/driftyco/ionic/commit/1c4d4a8b90e24a277187c7505538dbf461b95d11))
|
||||
* **buttonIcon:**
|
||||
* fix vertical-align in safari, closes #554 ([6acba8da](https://github.com/driftyco/ionic/commit/6acba8da3e02dc29c115748196bee93faa9a43b3))
|
||||
* float left icon-left, closes #515 ([38420c81](https://github.com/driftyco/ionic/commit/38420c81278f915562a0d0c941c83daf949ed2e0))
|
||||
* **click:** Clicks firing twice, closes #573 ([2132d292](https://github.com/driftyco/ionic/commit/2132d292e7bbf368e1c21be10b3ddf67a1b4e496))
|
||||
* **header:** Header icon button css fix for Safari, closes #576 ([801d2d7b](https://github.com/driftyco/ionic/commit/801d2d7b77d75c0288d7a039b2dfc0f5dbc99955))
|
||||
* **ionicScrollDelegate:** trigger resize before scrolling to top/bottom ([ea289b81](https://github.com/driftyco/ionic/commit/ea289b81c6a54d5adb3515b692d2dbec569d0498), closes [#522](https://github.com/driftyco/ionic/issues/522))
|
||||
* **list:** css: don't make last .list on page have margin-bottom ([fb5a0d4c](https://github.com/driftyco/ionic/commit/fb5a0d4c81461ef3c770f73a043284c71d4ac87d))
|
||||
* **listButtons:** Update list button sizes, closes #478 ([91652112](https://github.com/driftyco/ionic/commit/91652112a0eadf89fabdff852153e2ea88c340c5))
|
||||
* **navBar:** Remove duplicate back button arrows, closes #547 ([4808e80d](https://github.com/driftyco/ionic/commit/4808e80ddf7ce03947cee606376af6453bb52e2b))
|
||||
* **refresher:** make refresher css not create gap at end of list ([79387a4e](https://github.com/driftyco/ionic/commit/79387a4e4aa621e7d0e49b4062c94d63a503dfdb))
|
||||
* **scroll:** `<scroll>` is now registered with $ionicScrollDelegate ([2c7ce763](https://github.com/driftyco/ionic/commit/2c7ce7638563f97a0931a2cd3aa13632cbaf88f7))
|
||||
* **scroll-view:** css: make it take up only 100% height ([d2f9e94b](https://github.com/driftyco/ionic/commit/d2f9e94bcd7aec7292f2167c6bb20fca221b394a))
|
||||
* **scrollView:**
|
||||
* start scroll again if it stops beyond boundaries ([eed6b19b](https://github.com/driftyco/ionic/commit/eed6b19b519864b7b6a4cc4ef194be5423dde710))
|
||||
* nested scrollViews now work independently ([4cc4a18c](https://github.com/driftyco/ionic/commit/4cc4a18c66e3fb636a2b253b1a6b13b02ebee696), closes [#278](https://github.com/driftyco/ionic/issues/278))
|
||||
* **sideMenuContent:** make dragContent default to true ([61a280bd](https://github.com/driftyco/ionic/commit/61a280bda8b7604a5eda11e99a45f4b991a8e269))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$ionicScrollDelegate:**
|
||||
* add scrollTo(left,top,animate) to delegate ([c119498d](https://github.com/driftyco/ionic/commit/c119498d1bdfe9c5ec0d7453fce8f9a18227977c))
|
||||
* allow anchorScroll to animate with param ([36691bba](https://github.com/driftyco/ionic/commit/36691bbaebf51ae5252e5673d3392e3e6c724eb9))
|
||||
* add .anchorScroll() function ([c2bbd9e9](https://github.com/driftyco/ionic/commit/c2bbd9e96e2a14200e86fac3fdaa2408e19ab7ed))
|
||||
* **domUtil:** add getPositionInParent function ([a970f0bd](https://github.com/driftyco/ionic/commit/a970f0bdc3bf87cf26e984482e00d8e763fc326f))
|
||||
* **grid:** Added classes, variables and mixins for responsive grid options ([1cdb999e](https://github.com/driftyco/ionic/commit/1cdb999e5672391fbe02719eef060e28ea72871d))
|
||||
* **ionic:** remove angular-sanitize (ngSanitize) as dependency ([e7556233](https://github.com/driftyco/ionic/commit/e755623331c1ad2099b61cd07c93cb91ae992640))
|
||||
* **list:** reordering scrolls page, reordering performance better ([7f4b28d9](https://github.com/driftyco/ionic/commit/7f4b28d9da9e77a055f840d3b44384138af48ca2), closes [#521](https://github.com/driftyco/ionic/issues/521))
|
||||
* **sideMenuContent:** watch `drag-content` attribute ([7f9bfb5a](https://github.com/driftyco/ionic/commit/7f9bfb5a9499537f5aa18291b7c2043da2aced49))
|
||||
* **tabs:**
|
||||
* allow html in tab `title` attribute ([0facb120](https://github.com/driftyco/ionic/commit/0facb120c74f010a0a60e9e70c388b5b8264b890), closes [#528](https://github.com/driftyco/ionic/issues/528))
|
||||
* allow tab `badge-style` attribute to set badge class ([b11e0f51](https://github.com/driftyco/ionic/commit/b11e0f512bc332ed92aee1ce3fa4b8d331b6193e))
|
||||
* **toggle:**
|
||||
* Disable toggle w/ ng-disabled, closes #541 ([2eab747d](https://github.com/driftyco/ionic/commit/2eab747d47dbe3b299d7f3ed69fd790cad936e4e))
|
||||
* allow ngDisabled binding ([0fe44867](https://github.com/driftyco/ionic/commit/0fe4486737c9780b3c2d7c86d84eab57c961834c))
|
||||
|
||||
|
||||
|
||||
### 0.9.23 "Alpha Oxen" (2014-02-05)
|
||||
- Android back button correctly goes back a view or closes the app
|
||||
- CustomEvent polyfill improvements for Android
|
||||
- Fix tab icon alignments
|
||||
- Fix $ionicPlatform.ready()
|
||||
- Fire off ionic.Platform.ready() callbacks for both Cordova and non-cordova
|
||||
- Created ionic.Platform.exitApp();
|
||||
- Add major and minor platform version numbers in body css
|
||||
- Removed dist folder from git
|
||||
- Created release folder to hold the latest release
|
||||
- Automate bulding the nightly folder in the CDN
|
||||
- Clicking tab item takes user to root/home of the tab
|
||||
- Add tab badges with `badge` attribute
|
||||
- Remember the previous scroll of a page when going back to it
|
||||
|
||||
**Breaking Changes**
|
||||
|
||||
- `bower install ionic`: release files are now located in the `release` folder, not `dist`.
|
||||
|
||||
|
||||
### 0.9.22 "Alpha Narwhal" (2014-01-30)
|
||||
- Tap polyfill overhaul to remove 300ms delay when firing a click
|
||||
- Android click firing twice fixes
|
||||
- Fixes with the tap polyfill for directives using ng-click
|
||||
- Upgrade to Angular v1.2.10
|
||||
- Reduce default button height
|
||||
|
||||
|
||||
### 0.9.21 "Alpha Maine Coon" (2014-01-24)
|
||||
- Toggle directive now includes .item.item-toggle wrapper
|
||||
- Toggle/Checkbox/Radio implements ng-model/ng-value/ng-change
|
||||
- Ionicons v1.4.2, icons now using :before pseudo
|
||||
- Button and header size updates
|
||||
- Android "click" event firing twice fixes
|
||||
- Refactor platform ready event listeners
|
||||
- Refactor navView directive
|
||||
- Created ionic.Platform.fullscreen() and .showStatusBar()
|
||||
- Update to Angular v1.2.8
|
||||
- Disable pointer-events during transitions
|
||||
- Remove ngTouch from angular.modules
|
||||
- Remove angular-touch.js and angular-route.js references
|
||||
|
||||
|
||||
### 0.9.20 "Alpha Lynx" (2014-01-14)
|
||||
- Improved transitions between views
|
||||
- Fixed hide-nav-bar/hide-back-button view attributes
|
||||
- Removed title attributes from DOM
|
||||
- Remove nav title if the entering view doesn't have one
|
||||
- Fix padding being added to content directive
|
||||
- Rename ionic services to use $ionic prefix
|
||||
|
||||
|
||||
### 0.9.19 "Alpha Koala" (2014-01-10)
|
||||
- Created ViewState Service to track navigation history
|
||||
- Created navView directive
|
||||
- Removed navPage and navRouter directives
|
||||
- Using AngularUI Router instead of $route
|
||||
- Update examples to use $stateProvider instead $routeProvider
|
||||
|
||||
|
||||
### 0.9.18 "Alpha Jaguar" (2013-12-18)
|
||||
- Slide box bug fixes
|
||||
- Fixed issues with minification
|
||||
- Small tweaks
|
||||
|
||||
|
||||
### 0.9.17 "Alpha Iguana" (2013-12-16)
|
||||
- Nav bar button fixes
|
||||
- New slide box overhaul
|
||||
- New list overhaul
|
||||
- Radio button and checkbox fixes
|
||||
|
||||
|
||||
### 0.9.16 "Alpha Hippo" (2013-12-12)
|
||||
- Scrollbars
|
||||
- Scroll bug fixes
|
||||
|
||||
|
||||
### 0.9.15 "Alpha Giraffe" (2013-12-08)
|
||||
- Scroll view fixes
|
||||
- Radio button and checkbox fixes
|
||||
- Slide box fixes
|
||||
|
||||
|
||||
### 0.9.14 "Alpha Fox" (2013-12-04)
|
||||
- Massive scroll performance improvements
|
||||
- Android fixes and perf improvements
|
||||
- Header and nav bar button fixes
|
||||
- Animation performance improvements
|
||||
|
||||
|
||||
### 0.9.13 "Alpha Elephant" (2013-11-26)
|
||||
- nav router back button fixes
|
||||
- Remove back button on first route
|
||||
- Fix forward/back transition on iOS devices which lack history.state
|
||||
|
||||
|
||||
### 0.9.12 "Alpha Dog" (2013-11-24)
|
||||
- Fixed some overflow scrolling issues.
|
||||
|
||||
|
||||
### 0.9.11 "Alpha Cat Dog" (2013-11-23)
|
||||
|
||||
|
||||
### 0.9.10 "Alpha Cat" (2013-11-23)
|
||||
@@ -0,0 +1,23 @@
|
||||
Copyright 2014 Drifty Co.
|
||||
http://drifty.com/
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,129 @@
|
||||
<a href="https://travis-ci.org/driftyco/ionic"><img src="http://img.shields.io/travis/driftyco/ionic.svg?style=flat" data-bindattr-164="164" title="Build Status Images"></a>
|
||||
|
||||
[](https://waffle.io/driftyco/ionic)
|
||||
|
||||
The best place to start with Ionic is our [documentation page](http://ionicframework.com/docs/).
|
||||
|
||||
Ionic currently best supports iOS 6+ and Android 4.1+, with scaled-down support for Android 2.3.
|
||||
|
||||
# What is Ionic?
|
||||
|
||||
Ionic is the open source HTML5 Mobile Framework for building amazing, cross-platform hybrid native apps with HTML, JavaScript, and CSS.
|
||||
|
||||

|
||||
|
||||
We built Ionic because we wanted a framework that focused on building hybrid native apps, rather than mobile websites. We wanted this framework to be obsessive about great design and performance. A framework that left the past behind and focused on the future where mobile devices could make HTML5 feel native.
|
||||
|
||||
It's important to realize that Ionic is not a replacement for frameworks used for building mobile web apps. There are a lot
|
||||
of great solutions that work well for websites, like [jQuery Mobile](http://jquerymobile.com/).
|
||||
|
||||
Ionic is also not a good solution if you need to support older generation devices. Our [compatibility](http://ionicframework.com/docs/#browser-support) *starts* at iOS 6 and Android 4.1. We will never support versions earlier than those. This is a framework for the future. Learn more: [Where does the Ionic Framework fit in?](http://ionicframework.com/blog/where-does-the-ionic-framework-fit-in/)
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
To start using ionic, you have two options: copy over the built JS and CSS files, or
|
||||
use the `ionic` tool ([ionic-cli](https://github.com/driftyco/ionic-cli)) which can be installed through npm:
|
||||
|
||||
```bash
|
||||
$ sudo npm install -g ionic
|
||||
```
|
||||
|
||||
Then, you can start a new ionic project by running:
|
||||
|
||||
```bash
|
||||
$ ionic start myproject
|
||||
```
|
||||
|
||||
### Manual Start
|
||||
|
||||
- Download the latest **stable** release from:
|
||||
* The release folder of this repository
|
||||
* The Ionic CDN: [Latest Release](http://code.ionicframework.com/)
|
||||
* `bower install ionic`
|
||||
- Download the **bleeding edge just-from-master release** from:
|
||||
* The Ionic CDN: [Nightly Build](http://code.ionicframework.com/#nightly)
|
||||
* Look in the [ionic-bower Repository](https://github.com/driftyco/ionic-bower) for the latest version, and do for example `bower install driftyco/ionic-bower#0.9.23-alpha-652` (`bower install ionic` will have the latest available soon)
|
||||
|
||||
Once you have a release, use `js/ionic.js`, `js/ionic-angular.js`, and `css/ionic.css`.
|
||||
|
||||
For most cases, you'll need AngularJS as well. This is bundled in `js/angular/` and `js/angular-ui-router/`.
|
||||
|
||||
|
||||
## Demos
|
||||
|
||||
- [Ionic Codepen.io Demos](http://codepen.io/ionic/public-list)
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
* Follow [@ionicframework on Twitter](https://twitter.com/ionicframework).
|
||||
* Subscribe to the [Ionic Newsletter](http://ionicframework.com/subscribe/).
|
||||
* Have a question that's not a feature request or bug report? [Discuss on the Ionic Forum](http://forum.ionicframework.com/).
|
||||
* Read our [Blog](http://ionicframework.com/blog/).
|
||||
* Have a feature request or find a bug? [Submit an issue](http://ionicframework.com/submit-issue/).
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
**Max Lynch**
|
||||
|
||||
+ <https://twitter.com/maxlynch>
|
||||
+ <https://github.com/mlynch>
|
||||
|
||||
**Ben Sperry**
|
||||
|
||||
+ <https://twitter.com/benjsperry>
|
||||
+ <https://github.com/bensperry>
|
||||
|
||||
**Adam Bradley**
|
||||
|
||||
+ <https://twitter.com/adamdbradley>
|
||||
+ <https://github.com/adamdbradley>
|
||||
|
||||
**Andy Joslin**
|
||||
|
||||
+ <https://twitter.com/andytjoslin>
|
||||
+ <https://github.com/ajoslin>
|
||||
|
||||
## Development
|
||||
|
||||
* `npm install && npm install -g gulp protractor` to setup
|
||||
* (if you wish to run end-to-end tests): `webdriver-manager update --chrome` to install the webdriver.
|
||||
* `gulp` or `gulp build` to build
|
||||
* `gulp docs` to generate docs (read Documentation below for how to test docs locally).
|
||||
* `gulp build --release` to build with minification & strip debugs
|
||||
* `gulp watch` to watch and rebuild on change
|
||||
* `gulp karma` to test one-time
|
||||
* `gulp karma-watch` to test and re-run on source change
|
||||
* `gulp protractor` to test e2e tests locally
|
||||
* `gulp cloudtest` to run e2e tests in the cloud
|
||||
|
||||
### Documentation
|
||||
|
||||
* To test documentation, follow these steps:
|
||||
1. Clone ionic-site to `./tmp/ionic-site` - this is where the `gulp docs` task builds to. `./tmp` is the folder that travis uses to do all of its tasks.
|
||||
- `mkdir tmp && git clone git@github.com:driftyco/ionic-site tmp/ionic-site`
|
||||
2. Make jekyll rebuild whenever you change the site.
|
||||
- `cd tmp/ionic-site && jekyll serve -w`
|
||||
3. Go back to project root and build the docs
|
||||
- `gulp docs`
|
||||
4. Open localhost:4000 and see your changes! Re-run `gulp docs` again whenever you change something, and jekyll will update the site.
|
||||
|
||||
### Commit Conventions
|
||||
|
||||
* Uses http://github.com/ajoslin/conventional-changelog conventions
|
||||
|
||||
### Pushing New Release of Ionic
|
||||
|
||||
- Almost all of the logic for releasing Ionic is done on the Travis server
|
||||
- To push a new release:
|
||||
1. Update package.json version to new version
|
||||
2. Generate changelog with `gulp changelog`
|
||||
3. Go through the changelog, and fix any mistakes or clarify any unclear commit messages
|
||||
4. Commit package.json and CHANGELOG.md and push to master
|
||||
- Travis will detect that this commit changed the version in package.json and push out all necessary for this new release (tags, release files, site config, ...)
|
||||
|
||||
## LICENSE
|
||||
|
||||
Ionic is licensed under the MIT Open Source license. For more information, see the LICENSE file in this repository.
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "ionic",
|
||||
"version": "1.0.0-beta.4",
|
||||
"codename": "antimony-antelope",
|
||||
"homepage": "https://github.com/driftyco/ionic",
|
||||
"authors": [
|
||||
"Max Lynch <max@drifty.com>",
|
||||
"Adam Bradley <adam@drifty.com>",
|
||||
"Ben Sperry <ben@drifty.com>"
|
||||
],
|
||||
"description": "Advanced HTML5 hybrid mobile app development framework.",
|
||||
"main": [
|
||||
"release/css/ionic.css",
|
||||
"release/fonts/*",
|
||||
"release/js/ionic.js",
|
||||
"release/js/ionic-angular.js"
|
||||
],
|
||||
"keywords": [
|
||||
"mobile",
|
||||
"html5",
|
||||
"ionic",
|
||||
"cordova",
|
||||
"phonegap",
|
||||
"trigger",
|
||||
"triggerio",
|
||||
"angularjs",
|
||||
"angular"
|
||||
],
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"CONTRIBUTING.md",
|
||||
"Gruntfile.js",
|
||||
"component.json",
|
||||
"config",
|
||||
"examples",
|
||||
"package.json",
|
||||
"release/js/angular",
|
||||
"release/js/angular-ui",
|
||||
"scripts",
|
||||
"test",
|
||||
"js/controllers",
|
||||
"js/ext",
|
||||
"js/utils",
|
||||
"js/views",
|
||||
"js/_license.js",
|
||||
"js/ionic.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "~1.2.12",
|
||||
"angular-animate": "~1.2.12",
|
||||
"angular-sanitize": "~1.2.12",
|
||||
"angular-ui-router": "0.2.7"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user