try to apply new project structure

This commit is contained in:
2014-05-16 11:24:11 +08:00
parent 767862c2c1
commit 03e714a5ae
429 changed files with 164391 additions and 2559 deletions
+8 -2
View File
@@ -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
+18
View File
@@ -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"
}
+54
View File
@@ -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.
File diff suppressed because it is too large Load Diff
+27
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
{
"name": "angular-animate",
"version": "1.2.16",
"main": "./angular-animate.js",
"dependencies": {
"angular": "1.2.16"
}
}
+18
View File
@@ -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"
}
+54
View File
@@ -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
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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"
}
}
+18
View File
@@ -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"
}
+54
View File
@@ -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
View File
@@ -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>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</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>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
&lt;/div&gt;</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>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</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("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
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 &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</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,"&lt;");
// 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, '&amp;').
replace(NON_ALPHANUMERIC_REGEXP, function(value){
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* 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>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</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
View File
@@ -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,"&lt;"),e="textContent"in n?n.textContent:n.innerText;return a+e+d}function B(a){return a.replace(/&/g,
"&amp;").replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}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
+8
View File
@@ -0,0 +1,8 @@
{
"name": "angular-sanitize",
"version": "1.2.16",
"main": "./angular-sanitize.js",
"dependencies": {
"angular": "1.2.16"
}
}
+18
View File
@@ -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"
}
+11
View File
@@ -0,0 +1,11 @@
# master only
build
# common
node_modules
bower_components
.DS_Store
*~
# webstorm files
.idea
+13
View File
@@ -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
View File
@@ -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';
});
}
};
+21
View File
@@ -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.
+280
View File
@@ -0,0 +1,280 @@
# AngularUI Router &nbsp;[![Build Status](https://travis-ci.org/angular-ui/ui-router.png?branch=master)](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.
+8
View File
@@ -0,0 +1,8 @@
{
"name": "angular-ui-router",
"version": "0.2.7",
"main": "./release/angular-ui-router.js",
"dependencies": {
"angular": ">= 1.1.5"
}
}
+16
View File
@@ -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
View File
@@ -0,0 +1,6 @@
{
"plugins": [ "plugins/markdown" ],
"markdown": {
"parser": "gfm"
}
}
+66
View File
@@ -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
+63
View File
@@ -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"
}
}
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"> &rarr; {Promise.&lt;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.&lt;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.&lt;string, *></span>
</td>
<td class="attributes">
&lt;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.&lt;Object></span>
</td>
<td class="attributes">
&lt;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">
&lt;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.&lt;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"> &rarr; {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"> &rarr; {<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"> &rarr; {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 '&amp;'. 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"> &rarr; {<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&amp;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"> &rarr; {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&amp;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"> &rarr; {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"> &rarr; {Array.&lt;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.&lt;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 &lt; 0) ? Math.ceil(from) : Math.floor(from);
if (from &lt; 0) from += len;
for (; from &lt; 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&lt;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&lt;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.&lt;string, Function|string>} invocables functions to invoke or `$injector` services to fetch.
* @param {Object.&lt;string, *>} [locals] values to make available to the injectables
* @param {Promise.&lt;Object>} [parent] a promise returned by another call to `$resolve`.
* @param {Object} [self] the `this` for the invoked methods
* @return {Promise.&lt;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.&lt;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.&lt;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.&lt;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.&lt;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&lt;nPath; i++) values[params[i]] = m[i+1];
for (/**/; i&lt;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.&lt;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&lt;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&lt;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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+28
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
});
});
+44
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
});
});
});
+301
View File
@@ -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');
}));
});
+48
View File
@@ -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
View File
@@ -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);
});
});
});
+8
View File
@@ -0,0 +1,8 @@
describe('templateFactory', function () {
beforeEach(module('ui.router.util'));
it('exists', inject(function ($templateFactory) {
expect($templateFactory).toBeDefined();
}));
});
+94
View File
@@ -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'));
+142
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}));
});
});
+16
View File
@@ -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"
}
+48
View File
@@ -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.
+18
View File
@@ -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;
}
+21464
View File
File diff suppressed because it is too large Load Diff
+210
View File
@@ -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>&#160;</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
+7
View File
@@ -0,0 +1,7 @@
{
"name": "angular",
"version": "1.2.16",
"main": "./angular.js",
"dependencies": {
}
}
+65
View File
@@ -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"
}
+910
View File
@@ -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)
+23
View File
@@ -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.
+129
View File
@@ -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>
[![Issues Ready](https://badge.waffle.io/driftyco/ionic.png?label=ready&title=Ready)](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.
![Ionic GUI](http://ionicframework.com/img/gui_screen.jpg)
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.
+56
View File
@@ -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