text: /*
   AngularJS v1.3.0-beta.2
   (c) 2010-2014 Google, Inc. http://angularjs.org
   License: MIT
  */
  (function(D,U,s){'use strict';function F(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.0-beta.2/"+(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 xb(b){if(null==b||za(b))return!1;
  var a=b.length;return 1===b.nodeType&&a?!0:y(b)||I(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d;if(b)if(O(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!==r)b.forEach(a,c);else if(xb(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 Sb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Zc(b,
  a,c){for(var d=Sb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Tb(b){return function(a,c){b(c,a)}}function ab(){for(var b=ia.length,a;b;){b--;a=ia[b].charCodeAt(0);if(57==a)return ia[b]="A",ia.join("");if(90==a)ia[b]="0";else return ia[b]=String.fromCharCode(a+1),ia.join("")}ia.unshift("0");return ia.join("")}function Ub(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function t(b){var a=b.$$hashKey;r(arguments,function(a){a!==b&&r(a,function(a,c){b[c]=a})});Ub(b,a);return b}function R(b){return parseInt(b,
  10)}function Vb(b,a){return t(new (t(function(){},{prototype:b})),a)}function x(){}function Aa(b){return b}function $(b){return function(){return b}}function C(b){return"undefined"===typeof b}function v(b){return"undefined"!==typeof b}function X(b){return null!=b&&"object"===typeof b}function y(b){return"string"===typeof b}function yb(b){return"number"===typeof b}function na(b){return"[object Date]"===Ba.call(b)}function I(b){return"[object Array]"===Ba.call(b)}function O(b){return"function"===typeof b}
  function bb(b){return"[object RegExp]"===Ba.call(b)}function za(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function $c(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function ad(b,a,c){var d=[];r(b,function(b,f,h){d.push(a.call(c,b,f,h))});return d}function cb(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 Ma(b,a){var c=cb(b,a);0<=c&&b.splice(c,1);return a}function ba(b,a){if(za(b)||b&&b.$evalAsync&&b.$watch)throw Na("cpws");
  if(a){if(b===a)throw Na("cpi");if(I(b))for(var c=a.length=0;c<b.length;c++)a.push(ba(b[c]));else{c=a.$$hashKey;r(a,function(b,c){delete a[c]});for(var d in b)a[d]=ba(b[d]);Ub(a,c)}}else(a=b)&&(I(b)?a=ba(b,[]):na(b)?a=new Date(b.getTime()):bb(b)?a=RegExp(b.source):X(b)&&(a=ba(b,{})));return a}function Wb(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 ua(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(I(b)){if(!I(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ua(b[d],a[d]))return!1;return!0}}else{if(na(b))return na(a)&&b.getTime()==a.getTime();if(bb(b)&&bb(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||za(b)||za(a)||I(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!O(b[d])){if(!ua(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!O(a[d]))return!1;
  return!0}return!1}function Xb(){return U.securityPolicy&&U.securityPolicy.isActive||U.querySelector&&!(!U.querySelector("[ng-csp]")&&!U.querySelector("[data-ng-csp]"))}function db(b,a){var c=2<arguments.length?va.call(arguments,2):[];return!O(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(va.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function bd(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=
  s:za(a)?c="$WINDOW":a&&U===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function oa(b,a){return"undefined"===typeof b?s:JSON.stringify(b,bd,a?"  ":null)}function Yb(b){return y(b)?JSON.parse(b):b}function Oa(b){"function"===typeof b?b=!0:b&&0!==b.length?(b=P(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function fa(b){b=z(b).clone();try{b.empty()}catch(a){}var c=z("<div>").append(b).html();try{return 3===b[0].nodeType?P(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,
  function(a,b){return"<"+P(b)})}catch(d){return P(c)}}function Zb(b){try{return decodeURIComponent(b)}catch(a){}}function $b(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.split("="),d=Zb(c[0]),v(d)&&(b=v(c[1])?Zb(c[1]):!0,a[d]?I(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function ac(b){var a=[];r(b,function(b,d){I(b)?r(b,function(b){a.push(wa(d,!0)+(!0===b?"":"="+wa(b,!0)))}):a.push(wa(d,!0)+(!0===b?"":"="+wa(b,!0)))});return a.length?a.join("&"):""}function zb(b){return wa(b,
  !0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function wa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function cd(b,a){function c(a){a&&d.push(a)}var d=[b],e,f,h=["ng:app","ng-app","x-ng-app","data-ng-app"],g=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;r(h,function(a){h[a]=!0;c(U.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(r(b.querySelectorAll("."+a),c),r(b.querySelectorAll("."+
  a+"\\:"),c),r(b.querySelectorAll("["+a+"]"),c))});r(d,function(a){if(!e){var b=g.exec(" "+a.className+" ");b?(e=a,f=(b[2]||"").replace(/\s+/g,",")):r(a.attributes,function(b){!e&&h[b.name]&&(e=a,f=b.value)})}});e&&a(e,f?[f]:[])}function bc(b,a){var c=function(){b=z(b);if(b.injector()){var c=b[0]===U?"document":fa(b);throw Na("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=cc(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(D&&!d.test(D.name))return c();D.name=D.name.replace(d,"");Ca.resumeBootstrap=function(b){r(b,function(b){a.push(b)});c()}}function eb(b,a){a=a||"_";return b.replace(dd,function(b,d){return(d?a:"")+b.toLowerCase()})}function Ab(b,a,c){if(!b)throw Na("areq",a||"?",c||"required");return b}function Pa(b,a,c){c&&I(b)&&(b=b[b.length-1]);Ab(O(b),a,"not a function, got "+(b&&"object"==typeof b?
  b.constructor.name||"Object":typeof b));return b}function xa(b,a){if("hasOwnProperty"===b)throw Na("badname",a);}function dc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,h=0;h<f;h++)d=a[h],b&&(b=(e=b)[d]);return!c&&O(b)?db(e,b):b}function Bb(b){var a=b[0];b=b[b.length-1];if(a===b)return z(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return z(c)}function ed(b){var a=F("$injector"),c=F("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||F;return b.module||
  (b.module=function(){var b={};return function(e,f,h){if("hasOwnProperty"===e)throw c("badname","module");f&&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(!f)throw a("nomod",e);var c=[],d=[],l=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:f,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:l,run:function(a){d.push(a);return this}};h&&l(h);return n}())}}())}function Qa(b){return b.replace(fd,function(a,b,d,e){return e?d.toUpperCase():d}).replace(gd,"Moz$1")}function Cb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:[this],m=a,k,l,n,q,p,A;if(!d||null!=b)for(;e.length;)for(k=e.shift(),
  l=0,n=k.length;l<n;l++)for(q=z(k[l]),m?q.triggerHandler("$destroy"):m=!m,p=0,q=(A=q.children()).length;p<q;p++)e.push(Da(A[p]));return f.apply(this,arguments)}var f=Da.fn[b],f=f.$original||f;e.$original=f;Da.fn[b]=e}function S(b){if(b instanceof S)return b;y(b)&&(b=ca(b));if(!(this instanceof S)){if(y(b)&&"<"!=b.charAt(0))throw Db("nosel");return new S(b)}if(y(b)){var a=U.createElement("div");a.innerHTML="<div>&#160;</div>"+b;a.removeChild(a.firstChild);Eb(this,a.childNodes);z(U.createDocumentFragment()).append(this)}else Eb(this,
  b)}function Fb(b){return b.cloneNode(!0)}function Ea(b){ec(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ea(b[a])}function fc(b,a,c,d){if(v(d))throw Db("offargs");var e=ja(b,"events");ja(b,"handle")&&(C(a)?r(e,function(a,c){Gb(b,c,a);delete e[c]}):r(a.split(" "),function(a){C(c)?(Gb(b,a,e[a]),delete e[a]):Ma(e[a]||[],c)}))}function ec(b,a){var c=b[fb],d=Ra[c];d&&(a?delete Ra[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),fc(b)),delete Ra[c],b[fb]=s))}function ja(b,a,c){var d=
  b[fb],d=Ra[d||-1];if(v(c))d||(b[fb]=d=++hd,d=Ra[d]={}),d[a]=c;else return d&&d[a]}function gc(b,a,c){var d=ja(b,"data"),e=v(c),f=!e&&v(a),h=f&&!X(a);d||h||ja(b,"data",d={});if(e)d[a]=c;else if(f){if(h)return d&&d[a];t(d,a)}else return d}function Hb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function gb(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",ca((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g,
  " ").replace(" "+ca(a)+" "," ")))})}function hb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");r(a.split(" "),function(a){a=ca(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",ca(c))}}function Eb(b,a){if(a){a=a.nodeName||!v(a.length)||za(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function hc(b,a){return ib(b,"$"+(a||"ngController")+"Controller")}function ib(b,a,c){b=z(b);9==b[0].nodeType&&(b=b.find("html"));for(a=I(a)?a:[a];b.length;){for(var d=
  0,e=a.length;d<e;d++)if((c=b.data(a[d]))!==s)return c;b=b.parent()}}function ic(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ea(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function jc(b,a){var c=jb[a.toLowerCase()];return c&&kc[b.nodeName]&&c}function id(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(C(c.defaultPrevented)){var f=c.preventDefault;
  c.preventDefault=function(){c.defaultPrevented=!0;f.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var h=Wb(a[e||c.type]||[]);r(h,function(a){a.call(b,c)});8>=Q?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Fa(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=b.$$hashKey():c===
  s&&(c=b.$$hashKey=ab()):c=b;return a+":"+c}function Sa(b){r(b,this.put,this)}function lc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(jd,""),c=c.match(kd),r(c[1].split(ld),function(b){b.replace(md,function(b,c,d){a.push(d)})})),b.$inject=a):I(b)?(c=b.length-1,Pa(b[c],"fn"),a=b.slice(0,c)):Pa(b,"fn",!0);return a}function cc(b){function a(a){return function(b,c){if(X(b))r(b,Tb(a));else return a(b,c)}}function c(a,b){xa(a,"service");if(O(b)||I(b))b=n.instantiate(b);
  if(!b.$get)throw Ta("pget",a);return l[a+g]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,f,g;r(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(y(a))for(c=Ua(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,f=0,g=d.length;f<g;f++){var h=d[f],m=n.get(h[0]);m[h[1]].apply(m,h[2])}else O(a)?b.push(n.invoke(a)):I(a)?b.push(n.invoke(a)):Pa(a,"module")}catch(l){throw I(a)&&(a=a[a.length-1]),l.message&&(l.stack&&-1==l.stack.indexOf(l.message))&&(l=l.message+"\n"+l.stack),
  Ta("modulerr",a,l.stack||l.message||l);}}});return b}function f(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===h)throw Ta("cdep",m.join(" <- "));return a[d]}try{return m.unshift(d),a[d]=h,a[d]=b(d)}catch(e){throw a[d]===h&&delete a[d],e;}finally{m.shift()}}function d(a,b,e){var f=[],g=lc(a),h,k,m;k=0;for(h=g.length;k<h;k++){m=g[k];if("string"!==typeof m)throw Ta("itkn",m);f.push(e&&e.hasOwnProperty(m)?e[m]:c(m))}a.$inject||(a=a[h]);return a.apply(b,f)}return{invoke:d,instantiate:function(a,
  b){var c=function(){},e;c.prototype=(I(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return X(e)||O(e)?e:c},get:c,annotate:lc,has:function(b){return l.hasOwnProperty(b+g)||a.hasOwnProperty(b)}}}var h={},g="Provider",m=[],k=new Sa,l={$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,$(b))}),constant:a(function(a,b){xa(a,"constant");l[a]=b;q[a]=b}),decorator:function(a,b){var c=n.get(a+g),
  d=c.$get;c.$get=function(){var a=p.invoke(d,c);return p.invoke(b,null,{$delegate:a})}}}},n=l.$injector=f(l,function(){throw Ta("unpr",m.join(" <- "));}),q={},p=q.$injector=f(q,function(a){a=n.get(a+g);return p.invoke(a.$get,a)});r(e(b),function(a){p.invoke(a||x)});return p}function nd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;r(a,function(a){b||"a"!==P(a.nodeName)||(b=a)});return b}function f(){var b=
  c.hash(),d;b?(d=h.getElementById(b))?d.scrollIntoView():(d=e(h.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var h=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function od(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function pd(b,a,c,d){function e(a){try{a.apply(null,va.call(arguments,1))}finally{if(A--,0===A)for(;E.length;)try{E.pop()()}catch(b){c.error(b)}}}
  function f(a,b){(function lb(){r(B,function(a){a()});u=b(lb,a)})()}function h(){w=null;H!=g.url()&&(H=g.url(),r(Y,function(a){a(g.url())}))}var g=this,m=a[0],k=b.location,l=b.history,n=b.setTimeout,q=b.clearTimeout,p={};g.isMock=!1;var A=0,E=[];g.$$completeOutstandingRequest=e;g.$$incOutstandingRequestCount=function(){A++};g.notifyWhenNoOutstandingRequests=function(a){r(B,function(a){a()});0===A?a():E.push(a)};var B=[],u;g.addPollFn=function(a){C(u)&&f(100,n);B.push(a);return a};var H=k.href,J=a.find("base"),
  w=null;g.url=function(a,c){k!==b.location&&(k=b.location);l!==b.history&&(l=b.history);if(a){if(H!=a)return H=a,d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),J.attr("href",J.attr("href"))):(w=a,c?k.replace(a):k.href=a),g}else return w||k.href.replace(/%27/g,"'")};var Y=[],T=!1;g.onUrlChange=function(a){if(!T){if(d.history)z(b).on("popstate",h);if(d.hashchange)z(b).on("hashchange",h);else g.addPollFn(h);T=!0}Y.push(a);return a};g.baseHref=function(){var a=J.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,
  ""):""};var M={},aa="",V=g.baseHref();g.cookies=function(a,b){var d,e,f,g;if(a)b===s?m.cookie=escape(a)+"=;path="+V+";expires=Thu, 01 Jan 1970 00:00:00 GMT":y(b)&&(d=(m.cookie=escape(a)+"="+escape(b)+";path="+V).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==aa)for(aa=m.cookie,d=aa.split("; "),M={},f=0;f<d.length;f++)e=d[f],g=e.indexOf("="),0<g&&(a=unescape(e.substring(0,g)),M[a]===s&&(M[a]=unescape(e.substring(g+
  1))));return M}};g.defer=function(a,b){var c;A++;c=n(function(){delete p[c];e(a)},b||0);p[c]=!0;return c};g.defer.cancel=function(a){return p[a]?(delete p[a],q(a),e(x),!0):!1}}function qd(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new pd(b,d,a,c)}]}function rd(){this.$get=function(){function b(b,d){function e(a){a!=n&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,n),n=a,n.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw F("$cacheFactory")("iid",b);var h=
  0,g=t({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,l={},n=null,q=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=l[a]||(l[a]={key:a});e(c)}if(!C(b))return a in m||h++,m[a]=b,h>k&&this.remove(q.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;e(b)}return m[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;b==n&&(n=b.p);b==q&&(q=b.n);f(b.n,b.p);delete l[a]}delete m[a];h--},removeAll:function(){m={};h=0;l={};n=q=null},destroy:function(){l=
  g=m=null;delete a[b]},info:function(){return t({},g,{size:h})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function sd(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function mc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,f=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,h=/^<\s*(tr|th|td|thead|tbody|tfoot)(\s+[^>]*)?>/i,g=/^(on[a-z]+|formaction)$/;this.directive=function k(a,e){xa(a,"directive");
  y(a)?(Ab(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];r(c[a],function(c,f){try{var g=b.invoke(c);O(g)?g={compile:$(g)}:!g.compile&&g.link&&(g.compile=$(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||a;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){d(h)}});return e}])),c[a].push(e)):r(a,Tb(k));return this};this.aHrefSanitizationWhitelist=function(b){return v(b)?(a.aHrefSanitizationWhitelist(b),
  this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return v(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,n,q,p,A,E,B,u,H,J,w){function Y(a,b,c,d,e){a instanceof z||(a=z(a));r(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=z(b).wrap("<span></span>").parent()[0])});
  var f=M(a,b,a,c,d,e);T(a,"ng-scope");return function(b,c,d){Ab(b,"scope");var e=c?Ga.clone.call(a):a;r(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var g=e.length;d<g;d++){var h=e[d].nodeType;1!==h&&9!==h||e.eq(d).data("$scope",b)}c&&c(e,b);f&&f(b,e,e);return e}}function T(a,b){try{a.addClass(b)}catch(c){}}function M(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,n,p,q,A;f=c.length;var L=Array(f);for(p=0;p<f;p++)L[p]=c[p];A=p=0;for(q=h.length;p<q;A++)k=L[A],c=h[p++],f=h[p++],l=z(k),c?(c.scope?
  (n=a.$new(),l.data("$scope",n)):n=a,(l=c.transclude)||!e&&b?c(f,n,k,d,aa(a,l||b)):c(f,n,k,d,e)):f&&f(a,k.childNodes,s,e)}for(var h=[],k,l,n,p,q=0;q<a.length;q++)k=new Ib,l=V(a[q],[],k,0===q?d:s,e),(f=l.length?Va(l,a[q],k,b,c,null,[],[],f):null)&&f.scope&&T(z(a[q]),"ng-scope"),k=f&&f.terminal||!(n=a[q].childNodes)||!n.length?null:M(n,f?f.transclude:b),h.push(f,k),p=p||f||k,f=null;return p?g:null}function aa(a,b){return function(c,d,e){var f=!1;c||(c=a.$new(),f=c.$$transcluded=!0);d=b(c,d,e);if(f)d.on("$destroy",
  db(c,c.$destroy));return d}}function V(a,b,c,d,g){var h=c.$attr,k;switch(a.nodeType){case 1:v(b,ka(Ha(a).toLowerCase()),"E",d,g);var l,n,p;k=a.attributes;for(var q=0,A=k&&k.length;q<A;q++){var E=!1,H=!1;l=k[q];if(!Q||8<=Q||l.specified){n=l.name;p=ka(n);pa.test(p)&&(n=eb(p.substr(6),"-"));var B=p.replace(/(Start|End)$/,"");p===B+"Start"&&(E=n,H=n.substr(0,n.length-5)+"end",n=n.substr(0,n.length-6));p=ka(n.toLowerCase());h[p]=n;c[p]=l=ca(l.value);jc(a,p)&&(c[p]=!0);ga(a,b,l,p);v(b,p,"A",d,g,E,H)}}a=
  a.className;if(y(a)&&""!==a)for(;k=f.exec(a);)p=ka(k[2]),v(b,p,"C",d,g)&&(c[p]=ca(k[3])),a=a.substr(k.index+k[0].length);break;case 3:D(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))p=ka(k[1]),v(b,p,"M",d,g)&&(c[p]=ca(k[2]))}catch(w){}}b.sort(F);return b}function N(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ha("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 z(d)}function kb(a,
  b,c){return function(d,e,f,g,h){e=N(e[0],b,c);return a(d,e,f,g,h)}}function Va(a,c,d,e,f,g,h,k,p){function q(a,b,c,d){if(a){c&&(a=kb(a,c,d));a.require=G.require;if(M===G||G.$$isolateScope)a=nc(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=kb(b,c,d));b.require=G.require;if(M===G||G.$$isolateScope)b=nc(b,{isolateScope:!0});k.push(b)}}function H(a,b,c){var d,e="data",f=!1;if(y(a)){for(;"^"==(d=a.charAt(0))||"?"==d;)a=a.substr(1),"^"==d&&(e="inheritedData"),f=f||"?"==d;d=null;c&&"data"===e&&(d=c[a]);d=d||
  b[e]("$"+a+"Controller");if(!d&&!f)throw ha("ctreq",a,ga);}else I(a)&&(d=[],r(a,function(a){d.push(H(a,b,c))}));return d}function B(a,e,f,g,p){function q(a,b){var c;2>arguments.length&&(b=a,a=s);Ia&&(c=v);return p(a,b,c)}var L,w,u,Y,N,V,v={},mb;L=c===f?d:Wb(d,new Ib(z(f),d.$attr));w=L.$$element;if(M){var t=/^\s*([@=&])(\??)\s*(\w*)\s*$/;g=z(f);V=e.$new(!0);aa&&aa===M.$$originalDirective?g.data("$isolateScope",V):g.data("$isolateScopeNoTemplate",V);T(g,"ng-isolate-scope");r(M.scope,function(a,c){var d=
  a.match(t)||[],f=d[3]||c,g="?"==d[2],d=d[1],h,k,p,n;V.$$isolateBindings[c]=d+f;switch(d){case "@":L.$observe(f,function(a){V[c]=a});L.$$observers[f].$$scope=e;L[f]&&(V[c]=b(L[f])(e));break;case "=":if(g&&!L[f])break;k=A(L[f]);n=k.literal?ua:function(a,b){return a===b};p=k.assign||function(){h=V[c]=k(e);throw ha("nonassign",L[f],M.name);};h=V[c]=k(e);V.$watch(function(){var a=k(e);n(a,V[c])||(n(a,h)?p(e,a=V[c]):V[c]=a);return h=a},null,k.literal);break;case "&":k=A(L[f]);V[c]=function(a){return k(e,
  a)};break;default:throw ha("iscp",M.name,c,a);}})}mb=p&&q;J&&r(J,function(a){var b={$scope:a===M||a.$$isolateScope?V:e,$element:w,$attrs:L,$transclude:mb},c;N=a.controller;"@"==N&&(N=L[a.name]);c=E(N,b);v[a.name]=c;Ia||w.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});g=0;for(u=h.length;g<u;g++)try{Y=h[g],Y(Y.isolateScope?V:e,w,L,Y.require&&H(Y.require,w,v),mb)}catch(K){n(K,fa(w))}g=e;M&&(M.template||null===M.templateUrl)&&(g=V);a&&a(g,f.childNodes,s,p);for(g=k.length-
  1;0<=g;g--)try{Y=k[g],Y(Y.isolateScope?V:e,w,L,Y.require&&H(Y.require,w,v),mb)}catch(kb){n(kb,fa(w))}}p=p||{};for(var w=-Number.MAX_VALUE,u,J=p.controllerDirectives,M=p.newIsolateScopeDirective,aa=p.templateDirective,v=p.nonTlbTranscludeDirective,Va=!1,Ia=p.hasElementTranscludeDirective,K=d.$$element=z(c),G,ga,t,F=e,pa,D=0,Q=a.length;D<Q;D++){G=a[D];var R=G.$$start,W=G.$$end;R&&(K=N(c,R,W));t=s;if(w>G.priority)break;if(t=G.scope)u=u||G,G.templateUrl||(S("new/isolated scope",M,G,K),X(t)&&(M=G));ga=
  G.name;!G.templateUrl&&G.controller&&(t=G.controller,J=J||{},S("'"+ga+"' controller",J[ga],G,K),J[ga]=G);if(t=G.transclude)Va=!0,G.$$tlb||(S("transclusion",v,G,K),v=G),"element"==t?(Ia=!0,w=G.priority,t=N(c,R,W),K=d.$$element=z(U.createComment(" "+ga+": "+d[ga]+" ")),c=K[0],nb(f,z(va.call(t,0)),c),F=Y(t,e,w,g&&g.name,{nonTlbTranscludeDirective:v})):(t=z(Fb(c)).contents(),K.empty(),F=Y(t,e));if(G.template)if(S("template",aa,G,K),aa=G,t=O(G.template)?G.template(K,d):G.template,t=oc(t),G.replace){g=
  G;t=C(t);c=t[0];if(1!=t.length||1!==c.nodeType)throw ha("tplrt",ga,"");nb(f,K,c);Q={$attr:{}};t=V(c,[],Q);var Z=a.splice(D+1,a.length-(D+1));M&&lb(t);a=a.concat(t).concat(Z);x(d,Q);Q=a.length}else K.html(t);if(G.templateUrl)S("template",aa,G,K),aa=G,G.replace&&(g=G),B=P(a.splice(D,a.length-D),K,d,f,F,h,k,{controllerDirectives:J,newIsolateScopeDirective:M,templateDirective:aa,nonTlbTranscludeDirective:v}),Q=a.length;else if(G.compile)try{pa=G.compile(K,d,F),O(pa)?q(null,pa,R,W):pa&&q(pa.pre,pa.post,
  R,W)}catch($){n($,fa(K))}G.terminal&&(B.terminal=!0,w=Math.max(w,G.priority))}B.scope=u&&!0===u.scope;B.transclude=Va&&F;p.hasElementTranscludeDirective=Ia;return B}function lb(a){for(var b=0,c=a.length;b<c;b++)a[b]=Vb(a[b],{$$isolateScope:!0})}function v(b,e,f,g,h,l,p){if(e===h)return null;h=null;if(c.hasOwnProperty(e)){var q;e=a.get(e+d);for(var A=0,E=e.length;A<E;A++)try{q=e[A],(g===s||g>q.priority)&&-1!=q.restrict.indexOf(f)&&(l&&(q=Vb(q,{$$start:l,$$end:p})),b.push(q),h=q)}catch(H){n(H)}}return h}
  function x(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(T(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function C(a){var b;a=ca(a);if(b=h.exec(a)){b=b[1].toLowerCase();a=z("<table>"+a+"</table>");if(/(thead|tbody|tfoot)/.test(b))return a.children(b);
  a=a.children("tbody");return"tr"===b?a.children("tr"):a.children("tr").contents()}return z("<div>"+a+"</div>").contents()}function P(a,b,c,d,e,f,g,h){var k=[],l,n,A=b[0],E=a.shift(),w=t({},E,{templateUrl:null,transclude:null,replace:null,$$originalDirective:E}),B=O(E.templateUrl)?E.templateUrl(b,c):E.templateUrl;b.empty();q.get(H.getTrustedResourceUrl(B),{cache:p}).success(function(p){var q,H;p=oc(p);if(E.replace){p=C(p);q=p[0];if(1!=p.length||1!==q.nodeType)throw ha("tplrt",E.name,B);p={$attr:{}};
  nb(d,b,q);var u=V(q,[],p);X(E.scope)&&lb(u);a=u.concat(a);x(c,p)}else q=A,b.html(p);a.unshift(w);l=Va(a,q,c,e,b,E,f,g,h);r(d,function(a,c){a==q&&(d[c]=b[0])});for(n=M(b[0].childNodes,e);k.length;){p=k.shift();H=k.shift();var J=k.shift(),Y=k.shift(),u=b[0];if(H!==A){var N=H.className;h.hasElementTranscludeDirective&&E.replace||(u=Fb(q));nb(J,z(H),u);T(z(u),N)}H=l.transclude?aa(p,l.transclude):Y;l(n,p,u,d,H)}k=null}).error(function(a,b,c,d){throw ha("tpload",d.url);});return function(a,b,c,d,e){k?(k.push(b),
  k.push(c),k.push(d),k.push(e)):l(n,b,c,d,e)}}function F(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 S(a,b,c,d){if(b)throw ha("multidir",b.name,c.name,a,fa(d));}function D(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:$(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);T(c.data("$binding",e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function Ia(a,b){if("srcdoc"==b)return H.HTML;var c=Ha(a);if("xlinkHref"==
  b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return H.RESOURCE_URL}function ga(a,c,d,e){var f=b(d,!0);if(f){if("multiple"===e&&"SELECT"===Ha(a))throw ha("selmulti",fa(a));c.push({priority:100,compile:function(){return{pre:function(c,d,h){d=h.$$observers||(h.$$observers={});if(g.test(e))throw ha("nodomevents");if(f=b(h[e],!0,Ia(a,e)))h[e]=f(c),(d[e]||(d[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||c).$watch(f,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,
  a)})}}}})}}function nb(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;break}f&&f.replaceChild(c,d);a=U.createDocumentFragment();a.appendChild(d);c[z.expando]=d[z.expando];d=1;for(e=b.length;d<e;d++)f=b[d],z(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function nc(a,b){return t(function(){return a.apply(null,arguments)},a,b)}var Ib=function(a,b){this.$$element=
  a;this.$attr=b||{}};Ib.prototype={$normalize:ka,$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=pc(a,b),d=pc(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=jc(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=eb(a,"-"));e=Ha(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=w(b,"src"===a);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&r(c[a],function(a){try{a(b)}catch(c){n(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);B.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var R=b.startSymbol(),W=b.endSymbol(),oc="{{"==R||"}}"==W?Aa:function(a){return a.replace(/\{\{/g,
  R).replace(/}}/g,W)},pa=/^ngAttr[A-Z]/;return Y}]}function ka(b){return Qa(b.replace(td,""))}function pc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var h=d[f],g=0;g<e.length;g++)if(h==e[g])continue a;c+=(0<c.length?" ":"")+h}return c}function ud(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){xa(a,"controller");X(a)?t(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,f){var h,g,m;y(e)&&(h=e.match(a),g=h[1],m=h[3],e=
  b.hasOwnProperty(g)?b[g]:dc(f.$scope,g,!0)||dc(d,g,!0),Pa(e,g,!0));h=c.instantiate(e,f);if(m){if(!f||"object"!=typeof f.$scope)throw F("$controller")("noscp",g||e.name,m);f.$scope[m]=h}return h}}]}function vd(){this.$get=["$window",function(b){return z(b.document)}]}function wd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function qc(b){var a={},c,d,e;if(!b)return a;r(b.split("\n"),function(b){e=b.indexOf(":");c=P(ca(b.substr(0,e)));d=ca(b.substr(e+1));c&&(a[c]=
  a[c]?a[c]+(", "+d):d)});return a}function rc(b){var a=X(b)?b:s;return function(c){a||(a=qc(b));return c?a[P(c)]||null:a}}function sc(b,a,c){if(O(c))return c(b,a);r(c,function(c){b=c(b,a)});return b}function xd(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){y(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=Yb(d)));return d}],transformRequest:[function(a){return X(a)&&"[object File]"!==Ba.call(a)?
  oa(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"},f=this.interceptors=[],h=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,q){function p(a){function c(a){var b=t({},a,{data:sc(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},f=function(a){function b(a){var c;r(a,function(b,d){O(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=t({},a.headers),f,g,c=t({},c.common,c[P(a.method)]);b(c);b(d);a:for(f in c){a=P(f);for(g in d)if(P(g)===a)continue a;d[f]=c[f]}return d}(a);t(d,a);d.headers=f;d.method=Ja(d.method);(a=Jb(d.url)?b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:s)&&(f[d.xsrfHeaderName||e.xsrfHeaderName]=a);var g=[function(a){f=a.headers;var b=sc(a.data,rc(f),a.transformRequest);
  C(a.data)&&r(f,function(a,b){"content-type"===P(b)&&delete f[b]});C(a.withCredentials)&&!C(e.withCredentials)&&(a.withCredentials=e.withCredentials);return A(a,b,f).then(c,c)},s],h=n.when(d);for(r(u,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=g.shift();var k=g.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 A(b,c,f){function h(a,b,c){u&&(200<=a&&300>a?u.put(s,[a,b,qc(c)]):u.remove(s));k(b,a,c);d.$$phase||d.$apply()}function k(a,c,d){c=Math.max(c,0);(200<=c&&300>c?q.resolve:q.reject)({data:a,status:c,headers:rc(d),config:b})}function m(){var a=cb(p.pendingRequests,b);-1!==a&&p.pendingRequests.splice(a,1)}var q=n.defer(),A=q.promise,u,r,s=E(b.url,b.params);p.pendingRequests.push(b);A.then(m,m);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==
  b.method)&&(u=X(b.cache)?b.cache:X(e.cache)?e.cache:B);if(u)if(r=u.get(s),v(r)){if(r.then)return r.then(m,m),r;I(r)?k(r[1],r[0],ba(r[2])):k(r,200,{})}else u.put(s,A);C(r)&&a(b.method,s,c,h,f,b.timeout,b.withCredentials,b.responseType);return A}function E(a,b){if(!b)return a;var c=[];Zc(b,function(a,b){null===a||C(a)||(I(a)||(a=[a]),r(a,function(a){X(a)&&(a=oa(a));c.push(wa(b)+"="+wa(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var B=c("$http"),u=[];r(f,function(a){u.unshift(y(a)?
  q.get(a):q.invoke(a))});r(h,function(a,b){var c=y(a)?q.get(a):q.invoke(a);u.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});p.pendingRequests=[];(function(a){r(arguments,function(a){p[a]=function(b,c){return p(t(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){p[a]=function(b,c,d){return p(t(d||{},{method:a,url:b,data:c}))}})})("post","put");p.defaults=e;return p}]}function yd(b){if(8>=Q&&(!b.match(/^(get|post|head|put|delete|options)$/i)||
  !D.XMLHttpRequest))return new D.ActiveXObject("Microsoft.XMLHTTP");if(D.XMLHttpRequest)return new D.XMLHttpRequest;throw F("$httpBackend")("noxhr");}function zd(){this.$get=["$browser","$window","$document",function(b,a,c){return Ad(b,yd,b.defer,a.angular.callbacks,c[0])}]}function Ad(b,a,c,d,e){function f(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;Q&&8>=Q?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&
  d()}:c.onload=c.onerror=function(){d()};e.body.appendChild(c);return d}var h=-1;return function(e,m,k,l,n,q,p,A){function E(){u=h;J&&J();w&&w.abort()}function B(a,d,e,f){T&&c.cancel(T);J=w=null;0===d&&(d=e?200:"file"==qa(m).protocol?404:0);a(1223==d?204:d,e,f);b.$$completeOutstandingRequest(x)}var u;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==P(e)){var H="_"+(d.counter++).toString(36);d[H]=function(a){d[H].data=a};var J=f(m.replace("JSON_CALLBACK","angular.callbacks."+H),function(){d[H].data?
  B(l,200,d[H].data):B(l,u||-2);d[H]=Ca.noop})}else{var w=a(e);w.open(e,m,!0);r(n,function(a,b){v(a)&&w.setRequestHeader(b,a)});w.onreadystatechange=function(){if(w&&4==w.readyState){var a=null,b=null;u!==h&&(a=w.getAllResponseHeaders(),b="response"in w?w.response:w.responseText);B(l,u||w.status,b,a)}};p&&(w.withCredentials=!0);if(A)try{w.responseType=A}catch(s){if("json"!==A)throw s;}w.send(k||null)}if(0<q)var T=c(E,q);else q&&q.then&&q.then(E)}}function Bd(){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 f(f,k,l){for(var n,q,p=0,A=[],E=f.length,B=!1,u=[];p<E;)-1!=(n=f.indexOf(b,p))&&-1!=(q=f.indexOf(a,n+h))?(p!=n&&A.push(f.substring(p,n)),A.push(p=c(B=f.substring(n+h,q))),p.exp=B,p=q+g,B=!0):(p!=E&&A.push(f.substring(p)),p=E);(E=A.length)||(A.push(""),E=1);if(l&&1<A.length)throw tc("noconcat",f);if(!k||B)return u.length=E,p=function(a){try{for(var b=0,c=E,g;b<c;b++)"function"==
  typeof(g=A[b])&&(g=g(a),g=l?e.getTrusted(l,g):e.valueOf(g),null===g||C(g)?g="":"string"!=typeof g&&(g=oa(g))),u[b]=g;return u.join("")}catch(h){a=tc("interr",f,h.toString()),d(a)}},p.exp=f,p.parts=A,p}var h=b.length,g=a.length;f.startSymbol=function(){return b};f.endSymbol=function(){return a};return f}]}function Cd(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,h,g,m){var k=a.setInterval,l=a.clearInterval,n=c.defer(),q=n.promise,p=0,A=v(m)&&!m;g=v(g)?g:0;q.then(null,null,d);
  q.$$intervalId=k(function(){n.notify(p++);0<g&&p>=g&&(n.resolve(p),l(q.$$intervalId),delete e[q.$$intervalId]);A||b.$apply()},h);e[q.$$intervalId]=n;return q}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 Dd(){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 uc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=zb(b[a]);return b.join("/")}function vc(b,a,c){b=qa(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=R(b.port)||Ed[b.protocol]||null}function wc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=qa(b,c);a.$$path=decodeURIComponent(d&&
  "/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=$b(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function la(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Wa(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Kb(b){return b.substr(0,Wa(b).lastIndexOf("/")+1)}function xc(b,a){this.$$html5=!0;a=a||"";var c=Kb(b);vc(b,this,b);this.$$parse=function(a){var e=la(c,a);if(!y(e))throw Lb("ipthprfx",
  a,c);wc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=ac(this.$$search),b=this.$$hash?"#"+zb(this.$$hash):"";this.$$url=uc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=la(b,d))!==s)return d=e,(e=la(a,e))!==s?c+(la("/",e)||e):b+d;if((e=la(c,d))!==s)return c+e;if(c==d+"/")return c}}function Mb(b,a){var c=Kb(b);vc(b,this,b);this.$$parse=function(d){var e=la(b,d)||la(c,d),e="#"==e.charAt(0)?la(a,e):
  this.$$html5?e:"";if(!y(e))throw Lb("ihshprfx",d,a);wc(e,this,b);d=this.$$path;var f=/^\/?.*?:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=ac(this.$$search),e=this.$$hash?"#"+zb(this.$$hash):"";this.$$url=uc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Wa(b)==Wa(a))return a}}function yc(b,a){this.$$html5=!0;Mb.apply(this,arguments);var c=Kb(b);
  this.$$rewrite=function(d){var e;if(b==Wa(d))return d;if(e=la(c,d))return b+a+e;if(c===d+"/")return c}}function ob(b){return function(){return this[b]}}function zc(b,a){return function(c){if(C(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Fd(){var b="",a=!1;this.hashPrefix=function(a){return v(a)?(b=a,this):b};this.html5Mode=function(b){return v(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function h(a){c.$broadcast("$locationChangeSuccess",
  g.absUrl(),a)}var g,m=d.baseHref(),k=d.url();a?(m=k.substring(0,k.indexOf("/",k.indexOf("//")+2))+(m||"/"),e=e.history?xc:yc):(m=Wa(k),e=Mb);g=new e(m,"#"+b);g.$$parse(g.$$rewrite(k));f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var b=z(a.target);"a"!==P(b[0].nodeName);)if(b[0]===f[0]||!(b=b.parent())[0])return;var e=b.prop("href");X(e)&&"[object SVGAnimatedString]"===e.toString()&&(e=qa(e.animVal).href);var h=g.$$rewrite(e);e&&(!b.attr("target")&&h&&!a.isDefaultPrevented())&&
  (a.preventDefault(),h!=d.url()&&(g.$$parse(h),c.$apply(),D.angular["ff-684208-preventDefault"]=!0))}});g.absUrl()!=k&&d.url(g.absUrl(),!0);d.onUrlChange(function(a){g.absUrl()!=a&&(c.$evalAsync(function(){var b=g.absUrl();g.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(g.$$parse(b),d.url(b)):h(b)}),c.$$phase||c.$digest())});var l=0;c.$watch(function(){var a=d.url(),b=g.$$replace;l&&a==g.absUrl()||(l++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",g.absUrl(),a).defaultPrevented?
  g.$$parse(a):(d.url(g.absUrl(),b),h(a))}));g.$$replace=!1;return l});return g}]}function Gd(){var b=!0,a=this;this.debugEnabled=function(a){return v(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||x;a=!1;try{a=!!e.apply}catch(m){}return a?function(){var a=
  [];r(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 da(b,a){if("constructor"===b)throw ya("isecfld",a);return b}function Xa(b,a){if(b){if(b.constructor===b)throw ya("isecfn",a);if(b.document&&b.location&&b.alert&&b.setInterval)throw ya("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw ya("isecdom",
  a);}return b}function pb(b,a,c,d,e){e=e||{};a=a.split(".");for(var f,h=0;1<a.length;h++){f=da(a.shift(),d);var g=b[f];g||(g={},b[f]=g);b=g;b.then&&e.unwrapPromises&&(ra(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}f=da(a.shift(),d);return b[f]=c}function Ac(b,a,c,d,e,f,h){da(b,f);da(a,f);da(c,f);da(d,f);da(e,f);return h.unwrapPromises?function(g,h){var k=h&&h.hasOwnProperty(b)?h:g,l;if(null==k)return k;(k=k[b])&&k.then&&(ra(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=
  a})),k=k.$$v);if(!a)return k;if(null==k)return s;(k=k[a])&&k.then&&(ra(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!c)return k;if(null==k)return s;(k=k[c])&&k.then&&(ra(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!d)return k;if(null==k)return s;(k=k[d])&&k.then&&(ra(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!e)return k;if(null==k)return s;(k=k[e])&&k.then&&(ra(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);
  return k}:function(f,h){var k=h&&h.hasOwnProperty(b)?h:f;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 Hd(b,a){da(b,a);return function(a,d){return null==a?s:(d&&d.hasOwnProperty(b)?d:a)[b]}}function Id(b,a,c){da(b,c);da(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 Bc(b,a,c){if(Nb.hasOwnProperty(b))return Nb[b];
  var d=b.split("."),e=d.length,f;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||2!==e)if(a.csp)f=6>e?Ac(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var g=0,h;do h=Ac(d[g++],d[g++],d[g++],d[g++],d[g++],c,a)(b,f),f=s,b=h;while(g<e);return h};else{var h="var p;\n";r(d,function(b,d){da(b,c);h+="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 h=h+"return s;",g=new Function("s","k","pw",h);g.toString=$(h);f=a.unwrapPromises?function(a,b){return g(a,b,ra)}:g}else f=Id(d[0],d[1],c);else f=Hd(d[0],c);"hasOwnProperty"!==b&&(Nb[b]=f);return f}function Jd(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=function(b){return v(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return v(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer",
  "$log",function(c,d,e){a.csp=d.csp;ra=function(b){a.logPromiseWarnings&&!Cc.hasOwnProperty(b)&&(Cc[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 Ob(a);e=(new Ya(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return x}}}]}function Kd(){this.$get=["$rootScope","$exceptionHandler",
  function(b,a){return Ld(function(a){b.$evalAsync(a)},a)}]}function Ld(b,a){function c(a){return a}function d(a){return h(a)}var e=function(){var h=[],k,l;return l={resolve:function(a){if(h){var c=h;h=s;k=f(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){l.resolve(g(a))},notify:function(a){if(h){var c=h;h.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,f,g){var l=e(),E=function(d){try{l.resolve((O(b)?
  b:c)(d))}catch(e){l.reject(e),a(e)}},B=function(b){try{l.resolve((O(f)?f:d)(b))}catch(c){l.reject(c),a(c)}},u=function(b){try{l.notify((O(g)?g:c)(b))}catch(d){a(d)}};h?h.push([E,B,u]):k.then(E,B,u);return l.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,f){var g=null;try{g=(a||c)()}catch(h){return b(h,!1)}return g&&O(g.then)?g.then(function(){return b(e,f)},function(a){return b(a,!1)}):
  b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},f=function(a){return a&&O(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},h=function(a){var b=e();b.reject(a);return b.promise},g=function(c){return{then:function(f,g){var h=e();b(function(){try{h.resolve((O(g)?g:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:h,when:function(g,k,l,n){var q=e(),p,A=function(b){try{return(O(k)?k:c)(b)}catch(d){return a(d),
  h(d)}},E=function(b){try{return(O(l)?l:d)(b)}catch(c){return a(c),h(c)}},B=function(b){try{return(O(n)?n:c)(b)}catch(d){a(d)}};b(function(){f(g).then(function(a){p||(p=!0,q.resolve(f(a).then(A,E,B)))},function(a){p||(p=!0,q.resolve(E(a)))},function(a){p||q.notify(B(a))})});return q.promise},all:function(a){var b=e(),c=0,d=I(a)?[]:{};r(a,function(a,e){c++;f(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 Md(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame,e=!!c,f=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)}};f.supported=e;return f}]}function Nd(){var b=10,a=F("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector",
  "$exceptionHandler","$parse","$browser",function(d,e,f,h){function g(){this.$id=ab();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 m(b){if(q.$$phase)throw a("inprog",q.$$phase);q.$$phase=b}function k(a,b){var c=f(a);Pa(c,b);return c}function l(a,b,c){do a.$$listenerCount[c]-=
  b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function n(){}g.prototype={constructor:g,$new:function(a){a?(a=new g,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=this.$$postDigestQueue):(a=function(){},a.prototype=this,a=new a,a.$id=ab());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"),f=this.$$watchers,g={fn:b,last:n,get:e,exp:a,eq:!!d};c=null;if(!O(b)){var h=k(b||x,"listener");g.fn=function(a,b,c){h(c)}}if("string"==typeof a&&e.constant){var l=g.fn;g.fn=function(a,b,c){l.call(this,a,b,c);Ma(f,g)}}f||(f=this.$$watchers=[]);f.unshift(g);return function(){Ma(f,g);c=null}},$watchCollection:function(a,b){var c=this,d,e,g=0,h=f(a),k=[],l={},m=0;return this.$watch(function(){e=h(c);var a,b;if(X(e))if(xb(e))for(d!==
  k&&(d=k,m=d.length=0,g++),a=e.length,m!==a&&(g++,d.length=m=a),b=0;b<a;b++)d[b]!==e[b]&&(g++,d[b]=e[b]);else{d!==l&&(d=l={},m=0,g++);a=0;for(b in e)e.hasOwnProperty(b)&&(a++,d.hasOwnProperty(b)?d[b]!==e[b]&&(g++,d[b]=e[b]):(m++,d[b]=e[b],g++));if(m>a)for(b in g++,d)d.hasOwnProperty(b)&&!e.hasOwnProperty(b)&&(m--,delete d[b])}else d!==e&&(d=e,g++);return g},function(){b(e,d,c)})},$digest:function(){var d,f,g,h,k=this.$$asyncQueue,l=this.$$postDigestQueue,r,w,s=b,T,M=[],v,t,N;m("$digest");c=null;do{w=
  !1;for(T=this;k.length;){try{N=k.shift(),N.scope.$eval(N.expression)}catch(z){q.$$phase=null,e(z)}c=null}a:do{if(h=T.$$watchers)for(r=h.length;r--;)try{if(d=h[r])if((f=d.get(T))!==(g=d.last)&&!(d.eq?ua(f,g):"number"==typeof f&&"number"==typeof g&&isNaN(f)&&isNaN(g)))w=!0,c=d,d.last=d.eq?ba(f):f,d.fn(f,g===n?f:g,T),5>s&&(v=4-s,M[v]||(M[v]=[]),t=O(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,t+="; newVal: "+oa(f)+"; oldVal: "+oa(g),M[v].push(t));else if(d===c){w=!1;break a}}catch(y){q.$$phase=
  null,e(y)}if(!(h=T.$$childHead||T!==this&&T.$$nextSibling))for(;T!==this&&!(h=T.$$nextSibling);)T=T.$parent}while(T=h);if((w||k.length)&&!s--)throw q.$$phase=null,a("infdig",b,oa(M));}while(w||k.length);for(q.$$phase=null;l.length;)try{l.shift()()}catch(x){e(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==q&&(r(this.$$listenerCount,db(null,l,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=null)}},$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){q.$$phase||q.$$asyncQueue.length||h.defer(function(){q.$$asyncQueue.length&&q.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},
  $apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{q.$$phase=null;try{q.$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[cb(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=
  !0},defaultPrevented:!1},k=[h].concat(va.call(arguments,1)),l,m;do{d=f.$$listeners[a]||c;h.currentScope=f;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(n){e(n)}else d.splice(l,1),l--,m--;if(g)break;f=f.$parent}while(f);return h},$broadcast:function(a,b){for(var c=this,d=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(va.call(arguments,1)),h,k;c=d;){f.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,
  g)}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 f}};var q=new g;return q}]}function Od(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*(https?|ftp|file|blob):|data:image\//;this.aHrefSanitizationWhitelist=function(a){return v(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return v(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;if(!Q||
  8<=Q)if(f=qa(c).href,""!==f&&!f.match(e))return"unsafe:"+f;return c}}}function Pd(b){if("self"===b)return b;if(y(b)){if(-1<b.indexOf("***"))throw sa("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+b+"$")}if(bb(b))return RegExp("^"+b.source+"$");throw sa("imatcher");}function Dc(b){var a=[];v(b)&&r(b,function(b){a.push(Pd(b))});return a}function Qd(){this.SCE_CONTEXTS=ea;var b=["self"],a=[];
  this.resourceUrlWhitelist=function(a){arguments.length&&(b=Dc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Dc(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 sa("unsafe");};c.has("$sanitize")&&
  (e=c.get("$sanitize"));var f=d(),h={};h[ea.HTML]=d(f);h[ea.CSS]=d(f);h[ea.URL]=d(f);h[ea.JS]=d(f);h[ea.RESOURCE_URL]=d(h[ea.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw sa("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw sa("itype",a);return new c(b)},getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var f=h.hasOwnProperty(c)?h[c]:null;if(f&&d instanceof f)return d.$$unwrapTrustedValue();if(c===ea.RESOURCE_URL){var f=
  qa(d.toString()),l,n,q=!1;l=0;for(n=b.length;l<n;l++)if("self"===b[l]?Jb(f):b[l].exec(f.href)){q=!0;break}if(q)for(l=0,n=a.length;l<n;l++)if("self"===a[l]?Jb(f):a[l].exec(f.href)){q=!1;break}if(q)return d;throw sa("insecurl",d.toString());}if(c===ea.HTML)return e(d);throw sa("unsafe");},valueOf:function(a){return a instanceof f?a.$$unwrapTrustedValue():a}}}]}function Rd(){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 sa("iequirks");var e=ba(ea);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=Aa);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 f=e.parseAs,h=e.getTrusted,g=e.trustAs;r(ea,function(a,b){var c=P(b);e[Qa("parse_as_"+c)]=function(b){return f(a,b)};e[Qa("get_trusted_"+c)]=function(b){return h(a,
  b)};e[Qa("trust_as_"+c)]=function(b){return g(a,b)}});return e}]}function Sd(){this.$get=["$window","$document",function(b,a){var c={},d=R((/android (\d+)/.exec(P((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},h=f.documentMode,g,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,n=!1;if(k){for(var q in k)if(l=m.exec(q)){g=l[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||g+"Transition"in
  k);n=!!("animation"in k||g+"Animation"in k);!d||l&&n||(l=y(f.body.style.webkitTransition),n=y(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!h||7<h),hasEvent:function(a){if("input"==a&&9==Q)return!1;if(C(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Xb(),vendorPrefix:g,transitions:l,animations:n,android:d,msie:Q,msieDocumentMode:h}}]}function Td(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",
  function(b,a,c,d){function e(e,g,m){var k=c.defer(),l=k.promise,n=v(m)&&!m;g=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||b.$apply()},g);l.$$timeoutId=g;f[g]=k;return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function qa(b,a){var c=b;Q&&(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 Jb(b){b=y(b)?qa(b):b;return b.protocol===Ec.protocol&&b.host===Ec.host}function Ud(){this.$get=$(D)}function Fc(b){function a(d,e){if(X(d)){var f={};r(d,function(b,c){f[c]=a(c,b)});return f}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",Gc);a("date",Hc);a("filter",Vd);a("json",Wd);a("limitTo",Xd);a("lowercase",Yd);a("number",Ic);a("orderBy",Jc);a("uppercase",Zd)}function Vd(){return function(b,a,c){if(!I(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 Ca.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&$d.call(a,d)&&c(a[d],b[d]))return!0;
  return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!f(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)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(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 h in a)(function(b){"undefined"!=typeof a[b]&&e.push(function(c){return f("$"==b?c:c&&c[b],a[b])})})(h);break;case "function":e.push(a);break;default:return b}d=[];for(h=0;h<b.length;h++){var g=b[h];e.check(g)&&d.push(g)}return d}}function Gc(b){var a=b.NUMBER_FORMATS;return function(b,d){C(d)&&(d=a.CURRENCY_SYM);return Kc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Ic(b){var a=b.NUMBER_FORMATS;return function(b,d){return Kc(b,a.PATTERNS[0],
  a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Kc(b,a,c,d,e){if(null==b||!isFinite(b)||X(b))return"";var f=0>b;b=Math.abs(b);var h=b+"",g="",m=[],k=!1;if(-1!==h.indexOf("e")){var l=h.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&l[3]>e+1?h="0":(g=h,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(g=b.toFixed(e));else{h=(h.split(Lc)[1]||"").length;C(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));h=Math.pow(10,e);b=Math.round(b*h)/h;b=(""+b).split(Lc);h=b[0];b=b[1]||"";var l=0,n=a.lgSize,q=a.gSize;if(h.length>=n+q)for(l=h.length-
  n,k=0;k<l;k++)0===(l-k)%q&&0!==k&&(g+=c),g+=h.charAt(k);for(k=l;k<h.length;k++)0===(h.length-k)%n&&0!==k&&(g+=c),g+=h.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(g+=d+b.substr(0,e))}m.push(f?a.negPre:a.posPre);m.push(g);m.push(f?a.negSuf:a.posSuf);return m.join("")}function qb(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 Z(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 qb(e,a,d)}}
  function rb(b,a){return function(c,d){var e=c["get"+b](),f=Ja(a?"SHORT"+b:b);return d[f][e]}}function Mc(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Nc(b){return function(a){var c=Mc(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return qb(a,b)}}function Hc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,h=0,g=b[8]?a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&
  (f=R(b[9]+b[10]),h=R(b[9]+b[11]));g.call(a,R(b[1]),R(b[2])-1,R(b[3]));f=R(b[4]||0)-f;h=R(b[5]||0)-h;g=R(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,h,g,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 f="",h=[],g,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;y(c)&&(c=ae.test(c)?R(c):a(c));yb(c)&&(c=new Date(c));if(!na(c))return c;for(;e;)(m=be.exec(e))?(h=h.concat(va.call(m,1)),e=
  h.pop()):(h.push(e),e=null);r(h,function(a){g=ce[a];f+=g?g(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Wd(){return function(b){return oa(b,!0)}}function Xd(){return function(b,a){if(!I(b)&&!y(b))return b;a=R(a);if(y(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 Jc(b){return function(a,c,d){function e(a,
  b){return Oa(b)?function(b,c){return a(c,b)}:a}if(!I(a)||!c)return a;c=I(c)?c:[c];c=ad(c,function(a){var c=!1,d=a||Aa;if(y(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?("string"==f&&(c=c.toLowerCase(),e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},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 ta(b){O(b)&&(b={link:b});b.restrict=b.restrict||"AC";return $(b)}function Oc(b,a,c,d){function e(a,c){c=c?"-"+eb(c,"-"):"";d.removeClass(b,(a?sb:tb)+c);d.addClass(b,(a?tb:sb)+c)}var f=this,h=b.parent().controller("form")||ub,g=0,m=f.$error={},k=[];f.$name=a.name||a.ngForm;f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;h.$addControl(f);b.addClass(Ka);e(!0);f.$addControl=function(a){xa(a.$name,"input");k.push(a);a.$name&&(f[a.$name]=a)};f.$removeControl=function(a){a.$name&&
  f[a.$name]===a&&delete f[a.$name];r(m,function(b,c){f.$setValidity(c,!0,a)});Ma(k,a)};f.$setValidity=function(a,b,c){var d=m[a];if(b)d&&(Ma(d,c),d.length||(g--,g||(e(b),f.$valid=!0,f.$invalid=!1),m[a]=!1,e(!0,a),h.$setValidity(a,!0,f)));else{g||e(b);if(d){if(-1!=cb(d,c))return}else m[a]=d=[],g++,e(!1,a),h.$setValidity(a,!1,f);d.push(c);f.$valid=!1;f.$invalid=!0}};f.$setDirty=function(){d.removeClass(b,Ka);d.addClass(b,vb);f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.removeClass(b,
  vb);d.addClass(b,Ka);f.$dirty=!1;f.$pristine=!0;r(k,function(a){a.$setPristine()})}}function ma(b,a,c,d){b.$setValidity(a,c);return c?d:s}function de(b,a,c){var d=c.prop("validity");X(d)&&(c=function(c){if(b.$error[a]||!(d.badInput||d.customError||d.typeMismatch)||d.valueMissing)return c;b.$setValidity(a,!1)},b.$parsers.push(c),b.$formatters.push(c))}function Za(b,a,c,d,e,f){var h=a.prop("validity");if(!e.android){var g=!1;a.on("compositionstart",function(a){g=!0});a.on("compositionend",function(){g=
  !1;m()})}var m=function(){if(!g){var e=a.val();Oa(c.ngTrim||"T")&&(e=ca(e));if(d.$viewValue!==e||h&&""===e&&!h.valueMissing)b.$$phase?d.$setViewValue(e):b.$apply(function(){d.$setViewValue(e)})}};if(e.hasEvent("input"))a.on("input",m);else{var k,l=function(){k||(k=f.defer(function(){m();k=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||l()});if(e.hasEvent("paste"))a.on("paste cut",l)}a.on("change",m);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 ma(d,"pattern",d.$isEmpty(a)||n.test(a),a)}):e=function(c){var e=b.$eval(n);if(!e||!e.test)throw F("ngPattern")("noregexp",n,e,fa(a));return ma(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var q=R(c.ngMinlength);e=function(a){return ma(d,"minlength",d.$isEmpty(a)||a.length>=q,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var p=R(c.ngMaxlength);
  e=function(a){return ma(d,"maxlength",d.$isEmpty(a)||a.length<=p,a)};d.$parsers.push(e);d.$formatters.push(e)}}function wb(b,a){return function(c){var d;return na(c)?c:y(c)&&(b.lastIndex=0,c=b.exec(c))?(c.shift(),d={yyyy:0,MM:1,dd:1,HH:0,mm:0},r(c,function(b,c){c<a.length&&(d[a[c]]=+b)}),new Date(d.yyyy,d.MM-1,d.dd,d.HH,d.mm)):NaN}}function $a(b,a,c,d){return function(e,f,h,g,m,k,l){Za(e,f,h,g,m,k);g.$parsers.push(function(d){if(g.$isEmpty(d))return g.$setValidity(b,!0),null;if(a.test(d))return g.$setValidity(b,
  !0),c(d);g.$setValidity(b,!1);return s});g.$formatters.push(function(a){return na(a)?l("date")(a,d):""});h.min&&(e=function(a){var b=g.$isEmpty(a)||c(a)>=c(h.min);g.$setValidity("min",b);return b?a:s},g.$parsers.push(e),g.$formatters.push(e));h.max&&(e=function(a){var b=g.$isEmpty(a)||c(a)<=c(h.max);g.$setValidity("max",b);return b?a:s},g.$parsers.push(e),g.$formatters.push(e))}}function Pb(b,a){b="ngClass"+b;return function(){return{restrict:"AC",link:function(c,d,e){function f(b){if(!0===a||c.$index%
  2===a){var d=h(b||"");g?ua(b,g)||e.$updateClass(d,h(g)):e.$addClass(d)}g=ba(b)}function h(a){if(I(a))return a.join(" ");if(X(a)){var b=[];r(a,function(a,c){a&&b.push(c)});return b.join(" ")}return a}var g;c.$watch(e[b],f,!0);e.$observe("class",function(a){f(c.$eval(e[b]))});"ngClass"!==b&&c.$watch("$index",function(d,f){var g=d&1;if(g!==f&1){var n=h(c.$eval(e[b]));g===a?e.$addClass(n):e.$removeClass(n)}})}}}}var P=function(b){return y(b)?b.toLowerCase():b},$d=Object.prototype.hasOwnProperty,Ja=function(b){return y(b)?
  b.toUpperCase():b},Q,z,Da,va=[].slice,ee=[].push,Ba=Object.prototype.toString,Na=F("ng"),Ca=D.angular||(D.angular={}),Ua,Ha,ia=["0","0","0"];Q=R((/msie (\d+)/.exec(P(navigator.userAgent))||[])[1]);isNaN(Q)&&(Q=R((/trident\/.*; rv:(\d+)/.exec(P(navigator.userAgent))||[])[1]));x.$inject=[];Aa.$inject=[];var ca=function(){return String.prototype.trim?function(b){return y(b)?b.trim():b}:function(b){return y(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();Ha=9>Q?function(b){b=b.nodeName?b:b[0];return b.scopeName&&
  "HTML"!=b.scopeName?Ja(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var dd=/[A-Z]/g,fe={full:"1.3.0-beta.2",major:1,minor:3,dot:s,codeName:"undefined"},Ra=S.cache={},fb=S.expando="ng-"+(new Date).getTime(),hd=1,Pc=D.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Gb=D.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};S._data=
  function(b){return this.cache[b[this.expando]]||{}};var fd=/([\:\-\_]+(.))/g,gd=/^moz([A-Z])/,Db=F("jqLite"),Ga=S.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===U.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),S(D).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?z(this[b]):z(this[this.length+b])},length:0,push:ee,sort:[].sort,splice:[].splice},jb={};r("multiple selected checked disabled readOnly required open".split(" "),
  function(b){jb[P(b)]=b});var kc={};r("input select option textarea button form details".split(" "),function(b){kc[Ja(b)]=!0});r({data:gc,inheritedData:ib,scope:function(b){return z(b).data("$scope")||ib(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return z(b).data("$isolateScope")||z(b).data("$isolateScopeNoTemplate")},controller:hc,injector:function(b){return ib(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Hb,css:function(b,a,c){a=Qa(a);if(v(c))b.style[a]=
  c;else{var d;8>=Q&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=Q&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=P(a);if(jb[d])if(v(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||x).specified?d:s;else if(v(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(v(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];
  if(C(d))return e?b[e]:"";b[e]=d}var a=[];9>Q?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(C(a)){if("SELECT"===Ha(b)&&b.multiple){var c=[];r(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(C(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ea(d[c]);b.innerHTML=a},empty:ic},function(b,a){S.prototype[a]=function(a,d){var e,f;if(b!==ic&&(2==b.length&&
  b!==Hb&&b!==hc?a:d)===s){if(X(a)){for(e=0;e<this.length;e++)if(b===gc)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;f=e===s?Math.min(this.length,1):this.length;for(var h=0;h<f;h++){var g=b(this[h],a,d);e=e?e+g:g}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});r({removeData:ec,dealoc:Ea,on:function a(c,d,e,f){if(v(f))throw Db("onargs");var h=ja(c,"events"),g=ja(c,"handle");h||ja(c,"events",h={});g||ja(c,"handle",g=id(c,h));r(d.split(" "),function(d){var f=h[d];
  if(!f){if("mouseenter"==d||"mouseleave"==d){var l=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};h[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||g(a,d)})}else Pc(c,
  d,g),h[d]=[];f=h[d]}f.push(e)})},off:fc,one:function(a,c,d){a=z(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ea(a);r(new S(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){r(new S(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},
  prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;r(new S(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=z(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ea(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;r(new S(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:hb,removeClass:gb,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=d;C(f)&&(f=!Hb(a,c));(f?hb:gb)(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:Fb,triggerHandler:function(a,c,d){c=(ja(a,"events")||{})[c];d=d||[];var e=[{preventDefault:x,stopPropagation:x}];r(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){S.prototype[c]=function(c,e,f){for(var h,g=0;g<this.length;g++)C(h)?
  (h=a(this[g],c,e,f),v(h)&&(h=z(h))):Eb(h,a(this[g],c,e,f));return v(h)?h:this};S.prototype.bind=S.prototype.on;S.prototype.unbind=S.prototype.off});Sa.prototype={put:function(a,c){this[Fa(a)]=c},get:function(a){return this[Fa(a)]},remove:function(a){var c=this[a=Fa(a)];delete this[a];return c}};var kd=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,ld=/,/,md=/^\s*(_?)(\S+?)\1\s*$/,jd=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ta=F("$injector"),ge=F("$animate"),he=["$provide",function(a){this.$$selectors={};this.register=
  function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw ge("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,h,g){h?h.after(a):(c&&c[0]||(c=h.parent()),c.append(a));g&&d(g)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,g){this.enter(a,c,d,g)},addClass:function(a,
  c,h){c=y(c)?c:I(c)?c.join(" "):"";r(a,function(a){hb(a,c)});h&&d(h)},removeClass:function(a,c,h){c=y(c)?c:I(c)?c.join(" "):"";r(a,function(a){gb(a,c)});h&&d(h)},setClass:function(a,c,h,g){r(a,function(a){hb(a,c);gb(a,h)});g&&d(g)},enabled:x}}]}],ha=F("$compile");mc.$inject=["$provide","$$sanitizeUriProvider"];var td=/^(x[\:\-_]|data[\:\-_])/i,tc=F("$interpolate"),ie=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Ed={http:80,https:443,ftp:21},Lb=F("$location");yc.prototype=Mb.prototype=xc.prototype={$$html5:!1,
  $$replace:!1,absUrl:ob("$$absUrl"),url:function(a,c){if(C(a))return this.$$url;var d=ie.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:ob("$$protocol"),host:ob("$$host"),port:ob("$$port"),path:zc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(y(a))this.$$search=$b(a);else if(X(a))this.$$search=a;else throw Lb("isrcharg");break;
  default:C(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:zc("$$hash",Aa),replace:function(){this.$$replace=!0;return this}};var ya=F("$parse"),Cc={},ra,La={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:x,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return v(d)?v(e)?d+e:d:v(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(v(d)?d:0)-(v(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)},"=":x,"===":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)}},je={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Ob=function(a){this.options=a};Ob.prototype={constructor:Ob,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),f=La[this.ch],h=La[d],g=La[e];g?(this.tokens.push({index:this.index,text:e,fn:g}),this.index+=3):h?(this.tokens.push({index:this.index,text:d,fn:h}),this.index+=2):f?(this.tokens.push({index:this.index,text:this.ch,fn:f,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=v(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw ya("lexerr",
  a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=P(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,f,h,g;this.index<this.text.length;){g=this.text.charAt(this.index);if("."===g||this.isIdent(g)||this.isNumber(g))"."===g&&(e=this.index),c+=g;else break;this.index++}if(e)for(f=this.index;f<this.text.length;){g=this.text.charAt(f);if("("===g){h=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(g))f++;else break}d={index:d,text:c};if(La.hasOwnProperty(c))d.fn=La[c],d.json=La[c];else{var m=Bc(c,this.options,this.text);d.fn=
  t(function(a,c){return m(a,c)},{assign:function(d,e){return pb(d,c,e,a.text,a.options)}})}this.tokens.push(d);h&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:h,json:!1}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var h=this.text.charAt(this.index),e=e+h;if(f)"u"===h?(h=this.text.substring(this.index+1,this.index+5),h.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+h+"]"),this.index+=
  4,d+=String.fromCharCode(parseInt(h,16))):d=(f=je[h])?d+f:d+h,f=!1;else if("\\"===h)f=!0;else{if(h===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=h}this.index++}this.throwError("Unterminated quote",c)}};var Ya=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};Ya.ZERO=function(){return 0};Ya.prototype={constructor:Ya,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 ya("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw ya("ueoe",this.text);return this.tokens[0]},peek:function(a,
  c,d,e){if(0<this.tokens.length){var f=this.tokens[0],h=f.text;if(h===a||h===c||h===d||h===e||!(a||c||d||e))return f}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 t(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return t(function(e,f){return a(e,
  f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return t(function(e,f){return c(e,f,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,f=0;f<a.length;f++){var h=a[f];h&&(e=h(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,g){g=[g];for(var m=0;m<d.length;m++)g.push(d[m](a,e));return c.apply(a,g)};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,f){return a.assign(d,c(d,f),f)}):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(Ya.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=Bc(d,this.options,this.text);return t(function(c,d,g){return e(g||a(c,d))},{assign:function(e,h,g){return pb(a(e,g),d,h,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return t(function(e,
  f){var h=a(e,f),g=d(e,f),m;if(!h)return s;(h=Xa(h[g],c.text))&&(h.then&&c.options.unwrapPromises)&&(m=h,"$$v"in h||(m.$$v=s,m.then(function(a){m.$$v=a})),h=h.$$v);return h},{assign:function(e,f,h){var g=d(e,h);return Xa(a(e,h),c.text)[g]=f}})},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(f,h){for(var g=[],m=c?c(f,h):f,k=0;k<d.length;k++)g.push(d[k](f,h));k=a(f,h,m)||x;Xa(m,e.text);
  Xa(k,e.text);g=k.apply?k.apply(m,g):k(g[0],g[1],g[2],g[3],g[4]);return Xa(g,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 t(function(c,d){for(var h=[],g=0;g<a.length;g++)h.push(a[g](c,d));return h},{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 t(function(c,d){for(var e={},m=0;m<a.length;m++){var k=a[m];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Nb={},sa=F("$sce"),ea={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},W=U.createElement("a"),Ec=qa(D.location.href,!0);Fc.$inject=["$provide"];Gc.$inject=["$locale"];Ic.$inject=["$locale"];var Lc=".",ce={yyyy:Z("FullYear",
  4),yy:Z("FullYear",2,0,!0),y:Z("FullYear",1),MMMM:rb("Month"),MMM:rb("Month",!0),MM:Z("Month",2,1),M:Z("Month",1,1),dd:Z("Date",2),d:Z("Date",1),HH:Z("Hours",2),H:Z("Hours",1),hh:Z("Hours",2,-12),h:Z("Hours",1,-12),mm:Z("Minutes",2),m:Z("Minutes",1),ss:Z("Seconds",2),s:Z("Seconds",1),sss:Z("Milliseconds",3),EEEE:rb("Day"),EEE:rb("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?"+":"")+(qb(Math[0<a?"floor":"ceil"](a/60),
  2)+qb(Math.abs(a%60),2))},ww:Nc(2),w:Nc(1)},be=/((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,ae=/^\-?\d+$/;Hc.$inject=["$locale"];var Yd=$(P),Zd=$(Ja);Jc.$inject=["$parse"];var ke=$({restrict:"E",compile:function(a,c){8>=Q&&(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 f="[object SVGAnimatedString]"===Ba.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||
  a.preventDefault()})}}}),Qb={};r(jb,function(a,c){if("multiple"!=a){var d=ka("ng-"+c);Qb[d]=function(){return{priority:100,link:function(a,f,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});r(["src","srcset","href"],function(a){var c=ka("ng-"+a);Qb[c]=function(){return{priority:99,link:function(d,e,f){var h=a,g=a;"href"===a&&"[object SVGAnimatedString]"===Ba.call(e.prop("href"))&&(g="xlinkHref",f.$attr[g]="xlink:href",h=null);f.$observe(c,function(a){a&&(f.$set(g,a),Q&&h&&e.prop(h,f[g]))})}}}});
  var ub={$addControl:x,$removeControl:x,$setValidity:x,$setDirty:x,$setPristine:x};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,f,h){if(!f.action){var g=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};Pc(e[0],"submit",g);e.on("$destroy",function(){c(function(){Gb(e[0],"submit",g)},0,!1)})}var m=e.parent().controller("form"),k=f.name||
  f.ngForm;k&&pb(a,k,h,k);if(m)e.on("$destroy",function(){m.$removeControl(h);k&&pb(a,k,s,k);t(h,ub)})}}}}}]},le=Qc(),me=Qc(!0),ne=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,oe=/^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i,pe=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Rc=/^(\d{4})-(\d{2})-(\d{2})$/,Sc=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/,Rb=/^(\d{4})-W(\d\d)$/,Tc=/^(\d{4})-(\d\d)$/,Uc=/^(\d\d):(\d\d)$/,Vc={text:Za,date:$a("date",Rc,wb(Rc,["yyyy",
  "MM","dd"]),"yyyy-MM-dd"),"datetime-local":$a("datetimelocal",Sc,wb(Sc,["yyyy","MM","dd","HH","mm"]),"yyyy-MM-ddTHH:mm"),time:$a("time",Uc,wb(Uc,["HH","mm"]),"HH:mm"),week:$a("week",Rb,function(a){if(na(a))return a;if(y(a)){Rb.lastIndex=0;var c=Rb.exec(a);if(c){a=+c[1];var d=+c[2],c=Mc(a),d=7*(d-1);return new Date(a,0,c.getDate()+d)}}return NaN},"yyyy-Www"),month:$a("month",Tc,wb(Tc,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,h){Za(a,c,d,e,f,h);e.$parsers.push(function(a){var c=e.$isEmpty(a);
  if(c||pe.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});de(e,"number",c);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return ma(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 ma(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return ma(e,
  "number",e.$isEmpty(a)||yb(a),a)})},url:function(a,c,d,e,f,h){Za(a,c,d,e,f,h);a=function(a){return ma(e,"url",e.$isEmpty(a)||ne.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,h){Za(a,c,d,e,f,h);a=function(a){return ma(e,"email",e.$isEmpty(a)||oe.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){C(d.name)&&c.attr("name",ab());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 f=d.ngTrueValue,h=d.ngFalseValue;y(f)||(f=!0);y(h)||(h=!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!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:h})},hidden:x,button:x,submit:x,reset:x,file:x},Wc=["$browser","$sniffer","$filter",function(a,c,d){return{restrict:"E",
  require:"?ngModel",link:function(e,f,h,g){g&&(Vc[P(h.type)]||Vc.text)(e,f,h,g,c,a,d)}}}],tb="ng-valid",sb="ng-invalid",Ka="ng-pristine",vb="ng-dirty",qe=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,f,h){function g(a,c){c=c?"-"+eb(c,"-"):"";h.removeClass(e,(a?sb:tb)+c);h.addClass(e,(a?tb:sb)+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 m=f(d.ngModel),k=m.assign;if(!k)throw F("ngModel")("nonassign",d.ngModel,fa(e));this.$render=x;this.$isEmpty=function(a){return C(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||ub,n=0,q=this.$error={};e.addClass(Ka);g(!0);this.$setValidity=function(a,c){q[a]!==!c&&(c?(q[a]&&n--,n||(g(!0),this.$valid=!0,this.$invalid=!1)):(g(!1),this.$invalid=!0,this.$valid=!1,n++),q[a]=!c,g(c,a),l.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;
  this.$pristine=!0;h.removeClass(e,vb);h.addClass(e,Ka)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,h.removeClass(e,Ka),h.addClass(e,vb),l.$setDirty());r(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,k(a,d),r(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var p=this;a.$watch(function(){var c=m(a);if(p.$modelValue!==c){var d=p.$formatters,e=d.length;for(p.$modelValue=c;e--;)c=d[e](c);p.$viewValue!==
  c&&(p.$viewValue=c,p.$render())}return c})}],re=function(){return{require:["ngModel","^?form"],controller:qe,link:function(a,c,d,e){var f=e[0],h=e[1]||ub;h.$addControl(f);a.$on("$destroy",function(){h.$removeControl(f)})}}},se=$({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),Xc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",
  !0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},te=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!C(a)){var c=[];a&&r(a.split(f),function(a){a&&c.push(ca(a))});return c}});e.$formatters.push(function(a){return I(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},ue=/^(true|false|\d+)$/,ve=function(){return{priority:100,
  compile:function(a,c){return ue.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},we=ta(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),xe=["$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)})}}],ye=["$sce","$parse",
  function(a,c){return function(d,e,f){e.addClass("ng-binding").data("$binding",f.ngBindHtml);var h=c(f.ngBindHtml);d.$watch(function(){return(h(d)||"").toString()},function(c){e.html(a.getTrustedHtml(h(d))||"")})}}],ze=Pb("",!0),Ae=Pb("Odd",0),Be=Pb("Even",1),Ce=ta({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),De=[function(){return{scope:!0,controller:"@",priority:500}}],Yc={};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
  function(a){var c=ka("ng-"+a);Yc[c]=["$parse",function(d){return{compile:function(e,f){var h=d(f[c]);return function(c,d,e){d.on(P(a),function(a){c.$apply(function(){h(c,{$event:a})})})}}}}]});var Ee=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,h){var g,m,k;c.$watch(e.ngIf,function(f){Oa(f)?m||(m=c.$new(),h(m,function(c){c[c.length++]=U.createComment(" end ngIf: "+e.ngIf+" ");g={clone:c};a.enter(c,d.parent(),d)})):(k&&(k.remove(),
  k=null),m&&(m.$destroy(),m=null),g&&(k=Bb(g.clone),a.leave(k,function(){k=null}),g=null))})}}}],Fe=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Ca.noop,compile:function(h,g){var m=g.ngInclude||g.src,k=g.onload||"",l=g.autoscroll;return function(g,h,p,r,s){var t=0,u,z,J,w=function(){z&&(z.remove(),z=null);u&&(u.$destroy(),u=null);J&&(e.leave(J,function(){z=null}),z=J,J=null)};g.$watch(f.parseAsResourceUrl(m),
  function(f){var m=function(){!v(l)||l&&!g.$eval(l)||d()},p=++t;f?(a.get(f,{cache:c}).success(function(a){if(p===t){var c=g.$new();r.template=a;a=s(c,function(a){w();e.enter(a,null,h,m)});u=c;J=a;u.$emit("$includeContentLoaded");g.$eval(k)}}).error(function(){p===t&&w()}),g.$emit("$includeContentRequested")):(w(),r.template=null)})}}}}],Ge=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],He=ta({priority:450,
  compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Ie=ta({terminal:!0,priority:1E3}),Je=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,h){var g=h.count,m=h.$attr.when&&f.attr(h.$attr.when),k=h.offset||0,l=e.$eval(m)||{},n={},q=c.startSymbol(),p=c.endSymbol(),s=/^when(Minus)?(.+)$/;r(h,function(a,c){s.test(c)&&(l[P(c.replace("when","").replace("Minus","-"))]=f.attr(h.$attr[c]))});r(l,function(a,e){n[e]=c(a.replace(d,q+g+"-"+k+p))});e.$watch(function(){var c=
  parseFloat(e.$eval(g));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-k));return n[c](e,f,!0)},function(a){f.text(a)})}}}],Ke=["$parse","$animate",function(a,c){var d=F("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,h,g,m){var k=h.ngRepeat,l=k.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,q,p,s,t,v,u={$id:Fa};if(!l)throw d("iexp",k);h=l[1];g=l[2];(l=l[3])?(n=a(l),q=function(a,c,d){v&&(u[v]=a);u[t]=c;u.$index=d;return n(e,
  u)}):(p=function(a,c){return Fa(c)},s=function(a){return a});l=h.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",h);t=l[3]||l[1];v=l[2];var H={};e.$watchCollection(g,function(a){var g,h,l=f[0],n,u={},y,N,x,C,F,K,I=[];if(xb(a))F=a,n=q||p;else{n=q||s;F=[];for(x in a)a.hasOwnProperty(x)&&"$"!=x.charAt(0)&&F.push(x);F.sort()}y=F.length;h=I.length=F.length;for(g=0;g<h;g++)if(x=a===F?g:F[g],C=a[x],C=n(x,C,g),xa(C,"`track by` id"),H.hasOwnProperty(C))K=H[C],delete H[C],u[C]=
  K,I[g]=K;else{if(u.hasOwnProperty(C))throw r(I,function(a){a&&a.scope&&(H[a.id]=a)}),d("dupes",k,C);I[g]={id:C};u[C]=!1}for(x in H)H.hasOwnProperty(x)&&(K=H[x],g=Bb(K.clone),c.leave(g),r(g,function(a){a.$$NG_REMOVED=!0}),K.scope.$destroy());g=0;for(h=F.length;g<h;g++){x=a===F?g:F[g];C=a[x];K=I[g];I[g-1]&&(l=I[g-1].clone[I[g-1].clone.length-1]);if(K.scope){N=K.scope;n=l;do n=n.nextSibling;while(n&&n.$$NG_REMOVED);K.clone[0]!=n&&c.move(Bb(K.clone),null,z(l));l=K.clone[K.clone.length-1]}else N=e.$new();
  N[t]=C;v&&(N[v]=x);N.$index=g;N.$first=0===g;N.$last=g===y-1;N.$middle=!(N.$first||N.$last);N.$odd=!(N.$even=0===(g&1));K.scope||m(N,function(a){a[a.length++]=U.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,z(l));l=a;K.scope=N;K.clone=a;u[K.id]=K})}H=u})}}}],Le=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Oa(c)?"removeClass":"addClass"](d,"ng-hide")})}}],Me=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Oa(c)?"addClass":"removeClass"](d,
  "ng-hide")})}}],Ne=ta(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Oe=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var h,g,m,k=[];c.$watch(e.ngSwitch||e.on,function(d){var n,q=k.length;if(0<q){if(m){for(n=0;n<q;n++)m[n].remove();m=null}m=[];for(n=0;n<q;n++){var p=g[n];k[n].$destroy();m[n]=p;a.leave(p,function(){m.splice(n,1);0===m.length&&(m=null)})}}g=
  [];k=[];if(h=f.cases["!"+d]||f.cases["?"])c.$eval(e.change),r(h,function(d){var e=c.$new();k.push(e);d.transclude(e,function(c){var e=d.element;g.push(c);a.enter(c,e.parent(),e)})})})}}}],Pe=ta({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),Qe=ta({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||
  [];e.cases["?"].push({transclude:f,element:c})}}),Re=ta({link:function(a,c,d,e,f){if(!f)throw F("ngTransclude")("orphan",fa(c));f(function(a){c.empty();c.append(a)})}}),Se=["$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=F("ngOptions"),Ue=$({terminal:!0}),Ve=["$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:x};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},l=e,n;m.databound=d.ngModel;m.init=function(a,c,d){l=a;n=d};m.addOption=function(c){xa(c,'"option value"');k[c]=!0;l.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};m.removeOption=function(a){this.hasOption(a)&&(delete k[a],l.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Fa(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",
  !0)};m.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){m.renderUnknownOption=x})}],link:function(e,h,g,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(y.parent()&&y.remove(),c.val(a),""===a&&x.prop("selected",!0)):C(a)&&x?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){y.parent()&&y.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new Sa(d.$viewValue);r(c.find("option"),
  function(c){c.selected=v(a.get(c.value))})};a.$watch(function(){ua(e,d.$viewValue)||(e=ba(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(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,y;t=g.$modelValue;y=z(e)||[];var A=n?Sb(y):y,C,B,D;B={};s=!1;var L,J;if(p)if(w&&I(t))for(s=new Sa([]),D=0;D<t.length;D++)B[m]=t[D],s.put(w(e,B),t[D]);else s=new Sa(t);for(D=0;C=A.length,
  D<C;D++){k=D;if(n){k=A[D];if("$"===k.charAt(0))continue;B[n]=k}B[m]=y[k];d=q(e,B)||"";(k=a[d])||(k=a[d]=[],c.push(d));p?d=v(s.remove(w?w(e,B):r(e,B))):(w?(d={},d[m]=t,d=w(e,d)===w(e,B)):d=t===r(e,B),s=s||d);L=l(e,B);L=v(L)?L:"";k.push({id:w?w(e,B):n?A[D]:D,label:L,selected:d})}p||(E||null===t?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));B=0;for(A=c.length;B<A;B++){d=c[B];k=a[d];x.length<=B?(t={element:F.clone().attr("label",d),label:k.label},y=[t],x.push(y),
  f.append(t.element)):(y=x[B],t=y[0],t.label!=d&&t.element.attr("label",t.label=d));L=null;D=0;for(C=k.length;D<C;D++)s=k[D],(d=y[D+1])?(L=d.element,d.label!==s.label&&L.text(d.label=s.label),d.id!==s.id&&L.val(d.id=s.id),L[0].selected!==s.selected&&L.prop("selected",d.selected=s.selected)):(""===s.id&&E?J=E:(J=u.clone()).val(s.id).attr("selected",s.selected).text(s.label),y.push({element:J,label:s.label,id:s.id,selected:s.selected}),L?L.after(J):t.element.append(J),L=J);for(D++;y.length>D;)y.pop().element.remove()}for(;x.length>
  B;)x.pop()[0].element.remove()}var k;if(!(k=t.match(d)))throw Te("iexp",t,fa(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],q=c(k[3]||""),r=c(k[2]?k[1]:m),z=c(k[7]),w=k[8]?c(k[8]):null,x=[[{element:f,label:""}]];E&&(a(E)(e),E.removeClass("ng-scope"),E.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=z(e)||[],d={},h,k,l,q,t,v,u;if(p)for(k=[],q=0,v=x.length;q<v;q++)for(a=x[q],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(r(e,d))}}else if(h=f.val(),"?"==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=r(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=r(e,d);g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(m[1]){var q=m[0];m=m[1];var p=g.multiple,t=g.ngOptions,E=!1,x,u=z(U.createElement("option")),F=z(U.createElement("optgroup")),y=u.clone();g=0;for(var w=h.children(),D=w.length;g<D;g++)if(""===w[g].value){x=E=w.eq(g);break}q.init(m,E,
  y);p&&(m.$isEmpty=function(a){return!a||0===a.length});t?n(e,h,m):p?l(e,h,m):k(e,h,m,q)}}}}],We=["$interpolate",function(a){var c={addOption:x,removeOption:x};return{restrict:"E",priority:100,compile:function(d,e){if(C(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),l=k.data("$selectController")||k.parent().data("$selectController");l&&l.databound?d.prop("selected",!1):l=c;f?a.$watch(f,function(a,c){e.$set("value",a);a!==c&&l.removeOption(c);l.addOption(a)}):
  l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],Xe=$({restrict:"E",terminal:!1});(Da=D.jQuery)?(z=Da,t(Da.fn,{scope:Ga.scope,isolateScope:Ga.isolateScope,controller:Ga.controller,injector:Ga.injector,inheritedData:Ga.inheritedData}),Cb("remove",!0,!0,!1),Cb("empty",!1,!1,!1),Cb("html",!1,!1,!0)):z=S;Ca.element=z;(function(a){t(a,{bootstrap:bc,copy:ba,extend:t,equals:ua,element:z,forEach:r,injector:cc,noop:x,bind:db,toJson:oa,fromJson:Yb,identity:Aa,isUndefined:C,isDefined:v,
  isString:y,isFunction:O,isObject:X,isNumber:yb,isElement:$c,isArray:I,version:fe,isDate:na,lowercase:P,uppercase:Ja,callbacks:{counter:0},$$minErr:F,$$csp:Xb});Ua=ed(D);try{Ua("ngLocale")}catch(c){Ua("ngLocale",[]).provider("$locale",Dd)}Ua("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Od});a.provider("$compile",mc).directive({a:ke,input:Wc,textarea:Wc,form:le,script:Se,select:Ve,style:Xe,option:We,ngBind:we,ngBindHtml:ye,ngBindTemplate:xe,ngClass:ze,ngClassEven:Be,ngClassOdd:Ae,
  ngCloak:Ce,ngController:De,ngForm:me,ngHide:Me,ngIf:Ee,ngInclude:Fe,ngInit:He,ngNonBindable:Ie,ngPluralize:Je,ngRepeat:Ke,ngShow:Le,ngStyle:Ne,ngSwitch:Oe,ngSwitchWhen:Pe,ngSwitchDefault:Qe,ngOptions:Ue,ngTransclude:Re,ngModel:re,ngList:te,ngChange:se,required:Xc,ngRequired:Xc,ngValue:ve}).directive({ngInclude:Ge}).directive(Qb).directive(Yc);a.provider({$anchorScroll:nd,$animate:he,$browser:qd,$cacheFactory:rd,$controller:ud,$document:vd,$exceptionHandler:wd,$filter:Fc,$interpolate:Bd,$interval:Cd,
  $http:xd,$httpBackend:zd,$location:Fd,$log:Gd,$parse:Jd,$rootScope:Nd,$q:Kd,$sce:Rd,$sceDelegate:Qd,$sniffer:Sd,$templateCache:sd,$timeout:Td,$window:Ud,$$rAF:Md,$$asyncCallback:od})}])})(Ca);z(U).ready(function(){cd(U,bc)})})(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
  
  /**
   * State-based routing for AngularJS
   * @version v0.2.10
   * @link http://angular-ui.github.com/
   * @license MIT License, http://www.opensource.org/licenses/MIT
   */
  "undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return I(new(I(function(){},{prototype:a})),b)}function e(a){return H(arguments,function(b){b!==a&&H(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function h(a,b,c,d){var e,h=f(c,d),i={},j=[];for(var k in h)if(h[k].params&&h[k].params.length){e=h[k].params;for(var l in e)g(j,e[l])>=0||(j.push(e[l]),i[e[l]]=a[e[l]])}return I({},i,b)}function i(a,b){var c={};return H(a,function(a){var d=b[a];c[a]=null!=d?String(d):null}),c}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e<c.length;e++){var f=c[e];if(a[f]!=b[f])return!1}return!0}function k(a,b){var c={};return H(a,function(a){c[a]=b[a]}),c}function l(a,b){var d=1,f=2,g={},h=[],i=g,j=I(a.when(g),{$$promises:g,$$values:g});this.study=function(g){function k(a,c){if(o[c]!==f){if(n.push(c),o[c]===d)throw n.splice(0,n.indexOf(c)),new Error("Cyclic dependency: "+n.join(" -> "));if(o[c]=d,E(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);H(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return F(a)&&a.then&&a.$$promises}if(!F(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return H(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!C(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;H(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!F(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=I({},d),s=1+m.length/3,t=!1;if(C(f.$$failure))return k(f.$$failure),p;f.$$values?(t=e(r,f.$$values),h()):(I(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return C(a.template)?this.fromString(a.template,b):C(a.templateUrl)?this.fromUrl(a.templateUrl,b):C(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return D(a)?a(b):a},this.fromUrl=function(c,d){return D(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),H(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function o(){this.compile=function(a){return new n(a)},this.isMatcher=function(a){return F(a)&&D(a.exec)&&D(a.format)&&D(a.concat)},this.$get=function(){return this}}function p(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return C(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!D(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(E(a)){var b=a;a=function(){return b}}else if(!D(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=E(f);if(E(e)&&(e=a.compile(e)),!h&&!D(f)&&!G(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),I(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:E(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),I(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(b){function d(b){var d=b(c,a);return d?(E(d)&&a.replace().url(d),!0):!1}if(!b||!b.defaultPrevented){var g,h=e.length;for(g=0;h>g;g++)if(d(e[g]))return;f&&d(f)}}return b.$on("$locationChangeSuccess",d),{sync:function(){d()}}}]}function q(a,e,f){function g(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){var d=E(a),e=d?a:a.name,f=g(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var l=w[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function m(a,b){x[a]||(x[a]=[]),x[a].push(b)}function n(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!E(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(w.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):E(b.parent)?b.parent:"";if(e&&!w[e])return m(e,b.self);for(var f in z)D(z[f])&&(b[f]=z[f](b,z.$delegates[f]));if(w[c]=b,!b[y]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){v.$current.navigable==b&&j(a,c)||v.transitionTo(b,a,{location:!1})}]),x[c])for(var g=0;g<x[c].length;g++)n(x[c][g]);return b}function o(a){return a.indexOf("*")>-1}function p(a){var b=a.split("."),c=v.$current.name.split(".");if("**"===b[0]&&(c=c.slice(c.indexOf(b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(c.indexOf(b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function q(a,b){return E(a)&&!C(b)?z[a]:D(b)&&E(a)?(z[a]&&!z.$delegates[a]&&(z.$delegates[a]=z[a]),z[a]=b,this):this}function r(a,b){return F(a)?b=a:b.name=a,n(b),this}function s(a,e,g,m,n,q,r,s,x){function z(){r.url()!==M&&(r.url(M),r.replace())}function A(a,c,d,f,h){var i=d?c:k(a.params,c),j={$stateParams:i};h.resolve=n.resolve(a.resolve,j,h.resolve,a);var l=[h.resolve.then(function(a){h.globals=a})];return f&&l.push(f),H(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return g.load(d,{view:c,locals:j,params:i,notify:!1})||""}],l.push(n.resolve(e,j,h.resolve,a).then(function(f){if(D(c.controllerProvider)||G(c.controllerProvider)){var g=b.extend({},e,j);f.$$controller=m.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,h[d]=f}))}),e.all(l).then(function(){return h})}var B=e.reject(new Error("transition superseded")),F=e.reject(new Error("transition prevented")),K=e.reject(new Error("transition aborted")),L=e.reject(new Error("transition failed")),M=r.url(),N=x.baseHref();return u.locals={resolve:null,globals:{$stateParams:{}}},v={params:{},current:u.self,$current:u,transition:null},v.reload=function(){v.transitionTo(v.current,q,{reload:!0,inherit:!1,notify:!1})},v.go=function(a,b,c){return this.transitionTo(a,b,I({inherit:!0,relative:v.$current},c))},v.transitionTo=function(b,c,f){c=c||{},f=I({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,k=v.$current,n=v.params,o=k.path,p=l(b,f.relative);if(!C(p)){var s={to:b,toParams:c,options:f};if(g=a.$broadcast("$stateNotFound",s,k.self,n),g.defaultPrevented)return z(),K;if(g.retry){if(f.$retry)return z(),L;var w=v.transition=e.when(g.retry);return w.then(function(){return w!==v.transition?B:(s.options.$retry=!0,v.transitionTo(s.to,s.toParams,s.options))},function(){return K}),z(),w}if(b=s.to,c=s.toParams,f=s.options,p=l(b,f.relative),!C(p)){if(f.relative)throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'");throw new Error("No such state '"+b+"'")}}if(p[y])throw new Error("Cannot transition to abstract state '"+b+"'");f.inherit&&(c=h(q,c||{},v.$current,p)),b=p;var x,D,E=b.path,G=u.locals,H=[];for(x=0,D=E[x];D&&D===o[x]&&j(c,n,D.ownParams)&&!f.reload;x++,D=E[x])G=H[x]=D.locals;if(t(b,k,G,f))return b.self.reloadOnSearch!==!1&&z(),v.transition=null,e.when(v.current);if(c=i(b.params,c||{}),f.notify&&(g=a.$broadcast("$stateChangeStart",b.self,c,k.self,n),g.defaultPrevented))return z(),F;for(var N=e.when(G),O=x;O<E.length;O++,D=E[O])G=H[O]=d(G),N=A(D,c,D===b,N,G);var P=v.transition=N.then(function(){var d,e,g;if(v.transition!==P)return B;for(d=o.length-1;d>=x;d--)g=o[d],g.self.onExit&&m.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=x;d<E.length;d++)e=E[d],e.locals=H[d],e.self.onEnter&&m.invoke(e.self.onEnter,e.self,e.locals.globals);if(v.transition!==P)return B;v.$current=b,v.current=b.self,v.params=c,J(v.params,q),v.transition=null;var h=b.navigable;return f.location&&h&&(r.url(h.url.format(h.locals.globals.$stateParams)),"replace"===f.location&&r.replace()),f.notify&&a.$broadcast("$stateChangeSuccess",b.self,c,k.self,n),M=r.url(),v.current},function(d){return v.transition!==P?B:(v.transition=null,a.$broadcast("$stateChangeError",b.self,c,k.self,n,d),z(),e.reject(d))});return P},v.is=function(a,d){var e=l(a);return C(e)?v.$current!==e?!1:C(d)&&null!==d?b.equals(q,d):!0:c},v.includes=function(a,d){if(E(a)&&o(a)){if(!p(a))return!1;a=v.$current.name}var e=l(a);if(!C(e))return c;if(!C(v.$current.includes[e.name]))return!1;var f=!0;return b.forEach(d,function(a,b){C(q[b])&&q[b]===a||(f=!1)}),f},v.href=function(a,b,c){c=I({lossy:!0,inherit:!1,absolute:!1,relative:v.$current},c||{});var d=l(a,c.relative);if(!C(d))return null;b=h(q,b||{},v.$current,d);var e=d&&c.lossy?d.navigable:d,g=e&&e.url?e.url.format(i(d.params,b||{})):null;return!f.html5Mode()&&g&&(g="#"+f.hashPrefix()+g),"/"!==N&&(f.html5Mode()?g=N.slice(0,-1)+g:c.absolute&&(g=N.slice(1)+g)),c.absolute&&g&&(g=r.protocol()+"://"+r.host()+(80==r.port()||443==r.port()?"":":"+r.port())+(!f.html5Mode()&&g?"/":"")+g),g},v.get=function(a,b){if(!C(a)){var c=[];return H(w,function(a){c.push(a.self)}),c}var d=l(a,b);return d&&d.self?d.self:null},v}function t(a,b,c,d){return a!==b||(c!==b.locals||d.reload)&&a.self.reloadOnSearch!==!1?void 0:!0}var u,v,w={},x={},y="abstract",z={parent:function(a){if(C(a.parent)&&a.parent)return l(a.parent);var b=/^(.+)\.[^.]+$/.exec(a.name);return b?l(b[1]):u},data:function(a){return a.parent&&a.parent.data&&(a.data=a.self.data=I({},a.parent.data,a.data)),a.data},url:function(a){var b=a.url;if(E(b))return"^"==b.charAt(0)?e.compile(b.substring(1)):(a.parent.navigable||u).url.concat(b);if(e.isMatcher(b)||null==b)return b;throw new Error("Invalid url '"+b+"' in state '"+a+"'")},navigable:function(a){return a.url?a:a.parent?a.parent.navigable:null},params:function(a){if(!a.params)return a.url?a.url.parameters():a.parent.params;if(!G(a.params))throw new Error("Invalid params in state '"+a+"'");if(a.url)throw new Error("Both params and url specicified in state '"+a+"'");return a.params},views:function(a){var b={};return H(C(a.views)?a.views:{"":a},function(c,d){d.indexOf("@")<0&&(d+="@"+a.parent.name),b[d]=c}),b},ownParams:function(a){if(!a.parent)return a.params;var b={};H(a.params,function(a){b[a]=!0}),H(a.parent.params,function(c){if(!b[c])throw new Error("Missing required parameter '"+c+"' in state '"+a.name+"'");b[c]=!1});var c=[];return H(b,function(a,b){a&&c.push(b)}),c},path:function(a){return a.parent?a.parent.path.concat(a):[]},includes:function(a){var b=a.parent?I({},a.parent.includes):{};return b[a.name]=!0,b},$delegates:{}};u=n({name:"",url:"^",views:null,"abstract":!0}),u.navigable=null,this.decorator=q,this.state=r,this.$get=s,s.$inject=["$rootScope","$q","$view","$injector","$resolve","$stateParams","$location","$urlRouter","$browser"]}function r(){function a(a,b){return{load:function(c,d){var e,f={template:null,controller:null,view:null,locals:null,notify:!0,async:!0,params:{}};return d=I(f,d),d.view&&(e=b.fromConfig(d.view,d.params,d.locals)),e&&d.notify&&a.$broadcast("$viewContentLoading",d),e}}}this.$get=a,a.$inject=["$rootScope","$templateFactory"]}function s(){var a=!1;this.useAnchorScroll=function(){a=!0},this.$get=["$anchorScroll","$timeout",function(b,c){return a?b:function(a){c(function(){a[0].scrollIntoView()},0,!1)}}]}function t(a,c,d){function e(){return c.has?function(a){return c.has(a)?c.get(a):null}:function(a){try{return c.get(a)}catch(b){return null}}}function f(a,b){var c=function(){return{enter:function(a,b,c){b.after(a),c()},leave:function(a,b){a.remove(),b()}}};if(i)return{enter:function(a,b,c){i.enter(a,null,b,c)},leave:function(a,b){i.leave(a,b)}};if(h){var d=h&&h(b,a);return{enter:function(a,b,c){d.enter(a,null,b),c()},leave:function(a,b){d.leave(a),b()}}}return c()}var g=e(),h=g("$animator"),i=g("$animate"),j={restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(c,e,g){return function(c,e,h){function i(){k&&(k.remove(),k=null),m&&(m.$destroy(),m=null),l&&(q.leave(l,function(){k=null}),k=l,l=null)}function j(f){var h=c.$new(),j=l&&l.data("$uiViewName"),k=j&&a.$current&&a.$current.locals[j];if(f||k!==n){var r=g(h,function(a){q.enter(a,e,function(){(b.isDefined(p)&&!p||c.$eval(p))&&d(a)}),i()});n=a.$current.locals[r.data("$uiViewName")],l=r,m=h,m.$emit("$viewContentLoaded"),m.$eval(o)}}var k,l,m,n,o=h.onload||"",p=h.autoscroll,q=f(h,c);c.$on("$stateChangeSuccess",function(){j(!1)}),c.$on("$viewContentLoading",function(){j(!1)}),j(!0)}}};return j}function u(a,b,c){return{restrict:"ECA",priority:-400,compile:function(d){var e=d.html();return function(d,f,g){var h=g.uiView||g.name||"",i=f.inheritedData("$uiView");h.indexOf("@")<0&&(h=h+"@"+(i?i.state.name:"")),f.data("$uiViewName",h);var j=c.$current,k=j&&j.locals[h];if(k){f.data("$uiView",{name:h,state:k.$$state}),f.html(k.$template?k.$template:e);var l=a(f.contents());if(k.$$controller){k.$scope=d;var m=b(k.$$controller,k);k.$$controllerAs&&(d[k.$$controllerAs]=m),f.data("$ngControllerController",m),f.children().data("$ngControllerController",m)}l(d)}}}}}function v(a){var b=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/);if(!b||4!==b.length)throw new Error("Invalid state ref '"+a+"'");return{state:b[1],paramExpr:b[3]||null}}function w(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function x(a,c){var d=["location","inherit","reload"];return{restrict:"A",require:"?^uiSrefActive",link:function(e,f,g,h){var i=v(g.uiSref),j=null,k=w(f)||a.$current,l="FORM"===f[0].nodeName,m=l?"action":"href",n=!0,o={relative:k},p=e.$eval(g.uiSrefOpts)||{};b.forEach(d,function(a){a in p&&(o[a]=p[a])});var q=function(b){if(b&&(j=b),n){var c=a.href(i.state,j,o);return h&&h.$$setStateInfo(i.state,j),c?void(f[0][m]=c):(n=!1,!1)}};i.paramExpr&&(e.$watch(i.paramExpr,function(a){a!==j&&q(a)},!0),j=e.$eval(i.paramExpr)),q(),l||f.bind("click",function(b){var d=b.which||b.button;d>1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target")||(c(function(){a.go(i.state,j,o)}),b.preventDefault())})}}}function y(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(d,e,f){function g(){a.$current.self===i&&h()?e.addClass(l):e.removeClass(l)}function h(){return!k||j(k,b)}var i,k,l;l=c(f.uiSrefActive||"",!1)(d),this.$$setStateInfo=function(b,c){i=a.get(b,w(e)),k=c,g()},d.$on("$stateChangeSuccess",g)}]}}function z(a){return function(b){return a.is(b)}}function A(a){return function(b){return a.includes(b)}}function B(a,b){function e(a){this.locals=a.locals.globals,this.params=this.locals.$stateParams}function f(){this.locals=null,this.params=null}function g(c,g){if(null!=g.redirectTo){var h,j=g.redirectTo;if(E(j))h=j;else{if(!D(j))throw new Error("Invalid 'redirectTo' in when()");h=function(a,b){return j(a,b.path(),b.search())}}b.when(c,h)}else a.state(d(g,{parent:null,name:"route:"+encodeURIComponent(c),url:c,onEnter:e,onExit:f}));return i.push(g),this}function h(a,b,d){function e(a){return""!==a.name?a:c}var f={routes:i,params:d,current:c};return b.$on("$stateChangeStart",function(a,c,d,f){b.$broadcast("$routeChangeStart",e(c),e(f))}),b.$on("$stateChangeSuccess",function(a,c,d,g){f.current=e(c),b.$broadcast("$routeChangeSuccess",e(c),e(g)),J(d,f.params)}),b.$on("$stateChangeError",function(a,c,d,f,g,h){b.$broadcast("$routeChangeError",e(c),e(f),h)}),f}var i=[];e.$inject=["$$state"],this.when=g,this.$get=h,h.$inject=["$state","$rootScope","$routeParams"]}var C=b.isDefined,D=b.isFunction,E=b.isString,F=b.isObject,G=b.isArray,H=b.forEach,I=b.extend,J=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),l.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",l),m.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",m),n.prototype.concat=function(a){return new n(this.sourcePath+a+this.sourceSearch)},n.prototype.toString=function(){return this.source},n.prototype.exec=function(a,b){var c=this.regexp.exec(a);if(!c)return null;var d,e=this.params,f=e.length,g=this.segments.length-1,h={};if(g!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(d=0;g>d;d++)h[e[d]]=c[d+1];for(;f>d;d++)h[e[d]]=b[e[d]];return h},n.prototype.parameters=function(){return this.params},n.prototype.format=function(a){var b=this.segments,c=this.params;if(!a)return b.join("");var d,e,f,g=b.length-1,h=c.length,i=b[0];for(d=0;g>d;d++)f=a[c[d]],null!=f&&(i+=encodeURIComponent(f)),i+=b[d+1];for(;h>d;d++)f=a[c[d]],null!=f&&(i+=(e?"&":"?")+c[d]+"="+encodeURIComponent(f),e=!0);return i},b.module("ui.router.util").provider("$urlMatcherFactory",o),p.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",p),q.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",q),r.$inject=[],b.module("ui.router.state").provider("$view",r),b.module("ui.router.state").provider("$uiViewScroll",s),t.$inject=["$state","$injector","$uiViewScroll"],u.$inject=["$compile","$controller","$state"],b.module("ui.router.state").directive("uiView",t),b.module("ui.router.state").directive("uiView",u),x.$inject=["$state","$timeout"],y.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",x).directive("uiSrefActive",y),z.$inject=["$state"],A.$inject=["$state"],b.module("ui.router.state").filter("isState",z).filter("includedByState",A),B.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",B).directive("ngView",t)}(window,window.angular);
  /**
   * @license AngularJS v1.3.0-beta.2
   * (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.
   *
   *   - `$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);
  
  /**
   * @license AngularJS v1.3.0-beta.2
   * (c) 2010-2014 Google, Inc. http://angularjs.org
   * License: MIT
   */
  (function(window, angular, undefined) {'use strict';
  
  /* jshint maxlen: false */
  
  /**
   * @ngdoc module
   * @name ngAnimate
   * @description
   *
   * # ngAnimate
   *
   * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives.
   *
   *
   * <div doc-module-components="ngAnimate"></div>
   *
   * # Usage
   *
   * To see animations in action, all that is required is to define the appropriate CSS classes
   * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
   * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
   * by using the `$animate` service.
   *
   * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
   *
   * | Directive                                                 | Supported Animations                               |
   * |---------------------------------------------------------- |----------------------------------------------------|
   * | {@link ng.directive:ngRepeat#usage_animations ngRepeat}         | enter, leave and move                              |
   * | {@link ngRoute.directive:ngView#usage_animations ngView}        | enter and leave                                    |
   * | {@link ng.directive:ngInclude#usage_animations ngInclude}       | enter and leave                                    |
   * | {@link ng.directive:ngSwitch#usage_animations ngSwitch}         | enter and leave                                    |
   * | {@link ng.directive:ngIf#usage_animations ngIf}                 | enter and leave                                    |
   * | {@link ng.directive:ngClass#usage_animations ngClass}           | add and remove                                     |
   * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide}    | add and remove (the ng-hide class value)           |
   * | {@link ng.directive:form#usage_animations form}                 | add and remove (dirty, pristine, valid, invalid & all other validations)                |
   * | {@link ng.directive:ngModel#usage_animations ngModel}           | add and remove (dirty, pristine, valid, invalid & all other validations)                |
   *
   * You can find out more information about animations upon visiting each directive page.
   *
   * Below is an example of how to apply animations to a directive that supports animation hooks:
   *
   * ```html
   * <style type="text/css">
   * .slide.ng-enter, .slide.ng-leave {
   *   -webkit-transition:0.5s linear all;
   *   transition:0.5s linear all;
   * }
   *
   * .slide.ng-enter { }        /&#42; starting animations for enter &#42;/
   * .slide.ng-enter-active { } /&#42; terminal animations for enter &#42;/
   * .slide.ng-leave { }        /&#42; starting animations for leave &#42;/
   * .slide.ng-leave-active { } /&#42; terminal animations for leave &#42;/
   * </style>
   *
   * <!--
   * the animate service will automatically add .ng-enter and .ng-leave to the element
   * to trigger the CSS transition/animations
   * -->
   * <ANY class="slide" ng-include="..."></ANY>
   * ```
   *
   * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
   * animation has completed.
   *
   * <h2>CSS-defined Animations</h2>
   * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
   * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
   * and can be used to play along with this naming structure.
   *
   * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
   *
   * ```html
   * <style type="text/css">
   * /&#42;
   *  The animate class is apart of the element and the ng-enter class
   *  is attached to the element once the enter animation event is triggered
   * &#42;/
   * .reveal-animation.ng-enter {
   *  -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
   *  transition: 1s linear all; /&#42; All other modern browsers and IE10+ &#42;/
   *
   *  /&#42; The animation preparation code &#42;/
   *  opacity: 0;
   * }
   *
   * /&#42;
   *  Keep in mind that you want to combine both CSS
   *  classes together to avoid any CSS-specificity
   *  conflicts
   * &#42;/
   * .reveal-animation.ng-enter.ng-enter-active {
   *  /&#42; The animation code itself &#42;/
   *  opacity: 1;
   * }
   * </style>
   *
   * <div class="view-container">
   *   <div ng-view class="reveal-animation"></div>
   * </div>
   * ```
   *
   * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
   *
   * ```html
   * <style type="text/css">
   * .reveal-animation.ng-enter {
   *   -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
   *   animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
   * }
   * &#64-webkit-keyframes enter_sequence {
   *   from { opacity:0; }
   *   to { opacity:1; }
   * }
   * &#64keyframes enter_sequence {
   *   from { opacity:0; }
   *   to { opacity:1; }
   * }
   * </style>
   *
   * <div class="view-container">
   *   <div ng-view class="reveal-animation"></div>
   * </div>
   * ```
   *
   * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
   *
   * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
   * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
   * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
   * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
   * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
   * has no CSS transition/animation classes applied to it.
   *
   * <h3>CSS Staggering Animations</h3>
   * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
   * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be
   * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
   * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
   * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
   *
   * ```css
   * .my-animation.ng-enter {
   *   /&#42; standard transition code &#42;/
   *   -webkit-transition: 1s linear all;
   *   transition: 1s linear all;
   *   opacity:0;
   * }
   * .my-animation.ng-enter-stagger {
   *   /&#42; this will have a 100ms delay between each successive leave animation &#42;/
   *   -webkit-transition-delay: 0.1s;
   *   transition-delay: 0.1s;
   *
   *   /&#42; in case the stagger doesn't work then these two values
   *    must be set to 0 to avoid an accidental CSS inheritance &#42;/
   *   -webkit-transition-duration: 0s;
   *   transition-duration: 0s;
   * }
   * .my-animation.ng-enter.ng-enter-active {
   *   /&#42; standard transition styles &#42;/
   *   opacity:1;
   * }
   * ```
   *
   * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
   * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
   * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
   * will also be reset if more than 10ms has passed after the last animation has been fired.
   *
   * The following code will issue the **ng-leave-stagger** event on the element provided:
   *
   * ```js
   * var kids = parent.children();
   *
   * $animate.leave(kids[0]); //stagger index=0
   * $animate.leave(kids[1]); //stagger index=1
   * $animate.leave(kids[2]); //stagger index=2
   * $animate.leave(kids[3]); //stagger index=3
   * $animate.leave(kids[4]); //stagger index=4
   *
   * $timeout(function() {
   *   //stagger has reset itself
   *   $animate.leave(kids[5]); //stagger index=0
   *   $animate.leave(kids[6]); //stagger index=1
   * }, 100, false);
   * ```
   *
   * Stagger animations are currently only supported within CSS-defined animations.
   *
   * <h2>JavaScript-defined Animations</h2>
   * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not
   * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
   *
   * ```js
   * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
   * var ngModule = angular.module('YourApp', ['ngAnimate']);
   * ngModule.animation('.my-crazy-animation', function() {
   *   return {
   *     enter: function(element, done) {
   *       //run the animation here and call done when the animation is complete
   *       return function(cancelled) {
   *         //this (optional) function will be called when the animation
   *         //completes or when the animation is cancelled (the cancelled
   *         //flag will be set to true if cancelled).
   *       };
   *     },
   *     leave: function(element, done) { },
   *     move: function(element, done) { },
   *
   *     //animation that can be triggered before the class is added
   *     beforeAddClass: function(element, className, done) { },
   *
   *     //animation that can be triggered after the class is added
   *     addClass: function(element, className, done) { },
   *
   *     //animation that can be triggered before the class is removed
   *     beforeRemoveClass: function(element, className, done) { },
   *
   *     //animation that can be triggered after the class is removed
   *     removeClass: function(element, className, done) { }
   *   };
   * });
   * ```
   *
   * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
   * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
   * the element's CSS class attribute value and then run the matching animation event function (if found).
   * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
   * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
   *
   * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
   * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
   * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
   * or transition code that is defined via a stylesheet).
   *
   */
  
  angular.module('ngAnimate', ['ng'])
  
    /**
     * @ngdoc provider
     * @name $animateProvider
     * @description
     *
     * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
     * When an animation is triggered, the $animate service will query the $animate service to find any animations that match
     * the provided name value.
     *
     * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
     *
     * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
     *
     */
  
    //this private service is only used within CSS-enabled animations
    //IE8 + IE9 do not support rAF natively, but that is fine since they
    //also don't support transitions and keyframes which means that the code
    //below will never be used by the two browsers.
    .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) {
      var bod = $document[0].body;
      return function(fn) {
        //the returned function acts as the cancellation function
        return $$rAF(function() {
          //the line below will force the browser to perform a repaint
          //so that all the animated elements within the animation frame
          //will be properly updated and drawn on screen. This is
          //required to perform multi-class CSS based animations with
          //Firefox. DO NOT REMOVE THIS LINE.
          var a = bod.offsetWidth + 1;
          fn();
        });
      };
    }])
  
    .config(['$provide', '$animateProvider', function($provide, $animateProvider) {
      var noop = angular.noop;
      var forEach = angular.forEach;
      var selectors = $animateProvider.$$selectors;
  
      var ELEMENT_NODE = 1;
      var NG_ANIMATE_STATE = '$$ngAnimateState';
      var NG_ANIMATE_CLASS_NAME = 'ng-animate';
      var rootAnimateState = {running: true};
  
      function extractElementNode(element) {
        for(var i = 0; i < element.length; i++) {
          var elm = element[i];
          if(elm.nodeType == ELEMENT_NODE) {
            return elm;
          }
        }
      }
  
      function stripCommentsFromElement(element) {
        return angular.element(extractElementNode(element));
      }
  
      function isMatchingElement(elm1, elm2) {
        return extractElementNode(elm1) == extractElementNode(elm2);
      }
  
      $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document',
                              function($delegate,   $injector,   $sniffer,   $rootElement,   $$asyncCallback,    $rootScope,   $document) {
  
        var globalAnimationCounter = 0;
        $rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
  
        // disable animations during bootstrap, but once we bootstrapped, wait again
        // for another digest until enabling animations. The reason why we digest twice
        // is because all structural animations (enter, leave and move) all perform a
        // post digest operation before animating. If we only wait for a single digest
        // to pass then the structural animation would render its animation on page load.
        // (which is what we're trying to avoid when the application first boots up.)
        $rootScope.$$postDigest(function() {
          $rootScope.$$postDigest(function() {
            rootAnimateState.running = false;
          });
        });
  
        var classNameFilter = $animateProvider.classNameFilter();
        var isAnimatableClassName = !classNameFilter
                ? function() { return true; }
                : function(className) {
                  return classNameFilter.test(className);
                };
  
        function lookup(name) {
          if (name) {
            var matches = [],
                flagMap = {},
                classes = name.substr(1).split('.');
  
            //the empty string value is the default animation
            //operation which performs CSS transition and keyframe
            //animations sniffing. This is always included for each
            //element animation procedure if the browser supports
            //transitions and/or keyframe animations
            if ($sniffer.transitions || $sniffer.animations) {
              classes.push('');
            }
  
            for(var i=0; i < classes.length; i++) {
              var klass = classes[i],
                  selectorFactoryName = selectors[klass];
              if(selectorFactoryName && !flagMap[klass]) {
                matches.push($injector.get(selectorFactoryName));
                flagMap[klass] = true;
              }
            }
            return matches;
          }
        }
  
        function animationRunner(element, animationEvent, className) {
          //transcluded directives may sometimes fire an animation using only comment nodes
          //best to catch this early on to prevent any animation operations from occurring
          var node = element[0];
          if(!node) {
            return;
          }
  
          var isSetClassOperation = animationEvent == 'setClass';
          var isClassBased = isSetClassOperation ||
                             animationEvent == 'addClass' ||
                             animationEvent == 'removeClass';
  
          var classNameAdd, classNameRemove;
          if(angular.isArray(className)) {
            classNameAdd = className[0];
            classNameRemove = className[1];
            className = classNameAdd + ' ' + classNameRemove;
          }
  
          var currentClassName = element.attr('class');
          var classes = currentClassName + ' ' + className;
          if(!isAnimatableClassName(classes)) {
            return;
          }
  
          var beforeComplete = noop,
              beforeCancel = [],
              before = [],
              afterComplete = noop,
              afterCancel = [],
              after = [];
  
          var animationLookup = (' ' + classes).replace(/\s+/g,'.');
          forEach(lookup(animationLookup), function(animationFactory) {
            var created = registerAnimation(animationFactory, animationEvent);
            if(!created && isSetClassOperation) {
              registerAnimation(animationFactory, 'addClass');
              registerAnimation(animationFactory, 'removeClass');
            }
          });
  
          function registerAnimation(animationFactory, event) {
            var afterFn = animationFactory[event];
            var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)];
            if(afterFn || beforeFn) {
              if(event == 'leave') {
                beforeFn = afterFn;
                //when set as null then animation knows to skip this phase
                afterFn = null;
              }
              after.push({
                event : event, fn : afterFn
              });
              before.push({
                event : event, fn : beforeFn
              });
              return true;
            }
          }
  
          function run(fns, cancellations, allCompleteFn) {
            var animations = [];
            forEach(fns, function(animation) {
              animation.fn && animations.push(animation);
            });
  
            var count = 0;
            function afterAnimationComplete(index) {
              if(cancellations) {
                (cancellations[index] || noop)();
                if(++count < animations.length) return;
                cancellations = null;
              }
              allCompleteFn();
            }
  
            //The code below adds directly to the array in order to work with
            //both sync and async animations. Sync animations are when the done()
            //operation is called right away. DO NOT REFACTOR!
            forEach(animations, function(animation, index) {
              var progress = function() {
                afterAnimationComplete(index);
              };
              switch(animation.event) {
                case 'setClass':
                  cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress));
                  break;
                case 'addClass':
                  cancellations.push(animation.fn(element, classNameAdd || className,     progress));
                  break;
                case 'removeClass':
                  cancellations.push(animation.fn(element, classNameRemove || className,  progress));
                  break;
                default:
                  cancellations.push(animation.fn(element, progress));
                  break;
              }
            });
  
            if(cancellations && cancellations.length === 0) {
              allCompleteFn();
            }
          }
  
          return {
            node : node,
            event : animationEvent,
            className : className,
            isClassBased : isClassBased,
            isSetClassOperation : isSetClassOperation,
            before : function(allCompleteFn) {
              beforeComplete = allCompleteFn;
              run(before, beforeCancel, function() {
                beforeComplete = noop;
                allCompleteFn();
              });
            },
            after : function(allCompleteFn) {
              afterComplete = allCompleteFn;
              run(after, afterCancel, function() {
                afterComplete = noop;
                allCompleteFn();
              });
            },
            cancel : function() {
              if(beforeCancel) {
                forEach(beforeCancel, function(cancelFn) {
                  (cancelFn || noop)(true);
                });
                beforeComplete(true);
              }
              if(afterCancel) {
                forEach(afterCancel, function(cancelFn) {
                  (cancelFn || noop)(true);
                });
                afterComplete(true);
              }
            }
          };
        }
  
        /**
         * @ngdoc service
         * @name $animate
         * @function
         *
         * @description
         * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
         * When any of these operations are run, the $animate service
         * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
         * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
         *
         * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
         * will work out of the box without any extra configuration.
         *
         * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
         *
         * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
         *
         */
        return {
          /**
           * @ngdoc method
           * @name $animate#enter
           * @function
           *
           * @description
           * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
           * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
           *
           * Below is a breakdown of each step that occurs during enter animation:
           *
           * | Animation Step                                                                               | What the element class attribute looks like |
           * |----------------------------------------------------------------------------------------------|---------------------------------------------|
           * | 1. $animate.enter(...) is called                                                             | class="my-animation"                        |
           * | 2. element is inserted into the parentElement element or beside the afterElement element     | class="my-animation"                        |
           * | 3. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
           * | 4. the .ng-enter class is added to the element                                               | class="my-animation ng-animate ng-enter"    |
           * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-enter"    |
           * | 6. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-enter"    |
           * | 7. the .ng-enter-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
           * | 8. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
           * | 9. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
           * | 10. The doneCallback() callback is fired (if provided)                                       | class="my-animation"                        |
           *
           * @param {DOMElement} element the element that will be the focus of the enter animation
           * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
           * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
           * @param {function()=} doneCallback the callback function that will be called once the animation is complete
          */
          enter : function(element, parentElement, afterElement, doneCallback) {
            this.enabled(false, element);
            $delegate.enter(element, parentElement, afterElement);
            $rootScope.$$postDigest(function() {
              element = stripCommentsFromElement(element);
              performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
            });
          },
  
          /**
           * @ngdoc method
           * @name $animate#leave
           * @function
           *
           * @description
           * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
           * the animation is started, the following CSS classes will be added for the duration of the animation:
           *
           * Below is a breakdown of each step that occurs during leave animation:
           *
           * | Animation Step                                                                               | What the element class attribute looks like |
           * |----------------------------------------------------------------------------------------------|---------------------------------------------|
           * | 1. $animate.leave(...) is called                                                             | class="my-animation"                        |
           * | 2. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
           * | 3. the .ng-leave class is added to the element                                               | class="my-animation ng-animate ng-leave"    |
           * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-leave"    |
           * | 5. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-leave"    |
           * | 6. the .ng-leave-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
           * | 7. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
           * | 8. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
           * | 9. The element is removed from the DOM                                                       | ...                                         |
           * | 10. The doneCallback() callback is fired (if provided)                                       | ...                                         |
           *
           * @param {DOMElement} element the element that will be the focus of the leave animation
           * @param {function()=} doneCallback the callback function that will be called once the animation is complete
          */
          leave : function(element, doneCallback) {
            cancelChildAnimations(element);
            this.enabled(false, element);
            $rootScope.$$postDigest(function() {
              performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() {
                $delegate.leave(element);
              }, doneCallback);
            });
          },
  
          /**
           * @ngdoc method
           * @name $animate#move
           * @function
           *
           * @description
           * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
           * add the element directly after the afterElement element if present. Then the move animation will be run. Once
           * the animation is started, the following CSS classes will be added for the duration of the animation:
           *
           * Below is a breakdown of each step that occurs during move animation:
           *
           * | Animation Step                                                                               | What the element class attribute looks like |
           * |----------------------------------------------------------------------------------------------|---------------------------------------------|
           * | 1. $animate.move(...) is called                                                              | class="my-animation"                        |
           * | 2. element is moved into the parentElement element or beside the afterElement element        | class="my-animation"                        |
           * | 3. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
           * | 4. the .ng-move class is added to the element                                                | class="my-animation ng-animate ng-move"     |
           * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-move"     |
           * | 6. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-move"     |
           * | 7. the .ng-move-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
           * | 8. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
           * | 9. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
           * | 10. The doneCallback() callback is fired (if provided)                                       | class="my-animation"                        |
           *
           * @param {DOMElement} element the element that will be the focus of the move animation
           * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
           * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
           * @param {function()=} doneCallback the callback function that will be called once the animation is complete
          */
          move : function(element, parentElement, afterElement, doneCallback) {
            cancelChildAnimations(element);
            this.enabled(false, element);
            $delegate.move(element, parentElement, afterElement);
            $rootScope.$$postDigest(function() {
              element = stripCommentsFromElement(element);
              performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
            });
          },
  
          /**
           * @ngdoc method
           * @name $animate#addClass
           *
           * @description
           * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
           * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
           * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
           * or keyframes are defined on the -add or base CSS class).
           *
           * Below is a breakdown of each step that occurs during addClass animation:
           *
           * | Animation Step                                                                                 | What the element class attribute looks like |
           * |------------------------------------------------------------------------------------------------|---------------------------------------------|
           * | 1. $animate.addClass(element, 'super') is called                                               | class="my-animation"                        |
           * | 2. $animate runs any JavaScript-defined animations on the element                              | class="my-animation ng-animate"             |
           * | 3. the .super-add class are added to the element                                               | class="my-animation ng-animate super-add"   |
           * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay    | class="my-animation ng-animate super-add"   |
           * | 5. $animate waits for 10ms (this performs a reflow)                                            | class="my-animation ng-animate super-add"   |
           * | 6. the .super, .super-add-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super super-add super-add-active"          |
           * | 7. $animate waits for X milliseconds for the animation to complete                             | class="my-animation super super-add super-add-active"  |
           * | 8. The animation ends and all generated CSS classes are removed from the element               | class="my-animation super"                  |
           * | 9. The super class is kept on the element                                                      | class="my-animation super"                  |
           * | 10. The doneCallback() callback is fired (if provided)                                         | class="my-animation super"                  |
           *
           * @param {DOMElement} element the element that will be animated
           * @param {string} className the CSS class that will be added to the element and then animated
           * @param {function()=} doneCallback the callback function that will be called once the animation is complete
          */
          addClass : function(element, className, doneCallback) {
            element = stripCommentsFromElement(element);
            performAnimation('addClass', className, element, null, null, function() {
              $delegate.addClass(element, className);
            }, doneCallback);
          },
  
          /**
           * @ngdoc method
           * @name $animate#removeClass
           *
           * @description
           * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
           * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
           * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
           * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
           *
           * Below is a breakdown of each step that occurs during removeClass animation:
           *
           * | Animation Step                                                                                | What the element class attribute looks like     |
           * |-----------------------------------------------------------------------------------------------|---------------------------------------------|
           * | 1. $animate.removeClass(element, 'super') is called                                           | class="my-animation super"                  |
           * | 2. $animate runs any JavaScript-defined animations on the element                             | class="my-animation super ng-animate"       |
           * | 3. the .super-remove class are added to the element                                           | class="my-animation super ng-animate super-remove"|
           * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay   | class="my-animation super ng-animate super-remove"   |
           * | 5. $animate waits for 10ms (this performs a reflow)                                           | class="my-animation super ng-animate super-remove"   |
           * | 6. the .super-remove-active and .ng-animate-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super-remove super-remove-active"          |
           * | 7. $animate waits for X milliseconds for the animation to complete                            | class="my-animation ng-animate ng-animate-active super-remove super-remove-active"   |
           * | 8. The animation ends and all generated CSS classes are removed from the element              | class="my-animation"                        |
           * | 9. The doneCallback() callback is fired (if provided)                                         | class="my-animation"                        |
           *
           *
           * @param {DOMElement} element the element that will be animated
           * @param {string} className the CSS class that will be animated and then removed from the element
           * @param {function()=} doneCallback the callback function that will be called once the animation is complete
          */
          removeClass : function(element, className, doneCallback) {
            element = stripCommentsFromElement(element);
            performAnimation('removeClass', className, element, null, null, function() {
              $delegate.removeClass(element, className);
            }, doneCallback);
          },
  
            /**
             *
             * @ngdoc function
             * @name $animate#setClass
             * @function
             * @description Adds and/or removes the given CSS classes to and from the element.
             * Once complete, the done() callback will be fired (if provided).
             * @param {DOMElement} element the element which will it's CSS classes changed
             *   removed from it
             * @param {string} add the CSS classes which will be added to the element
             * @param {string} remove the CSS class which will be removed from the element
             * @param {Function=} done the callback function (if provided) that will be fired after the
             *   CSS classes have been set on the element
             */
          setClass : function(element, add, remove, doneCallback) {
            element = stripCommentsFromElement(element);
            performAnimation('setClass', [add, remove], element, null, null, function() {
              $delegate.setClass(element, add, remove);
            }, doneCallback);
          },
  
          /**
           * @ngdoc method
           * @name $animate#enabled
           * @function
           *
           * @param {boolean=} value If provided then set the animation on or off.
           * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation
           * @return {boolean} Current animation state.
           *
           * @description
           * Globally enables/disables animations.
           *
          */
          enabled : function(value, element) {
            switch(arguments.length) {
              case 2:
                if(value) {
                  cleanup(element);
                } else {
                  var data = element.data(NG_ANIMATE_STATE) || {};
                  data.disabled = true;
                  element.data(NG_ANIMATE_STATE, data);
                }
              break;
  
              case 1:
                rootAnimateState.disabled = !value;
              break;
  
              default:
                value = !rootAnimateState.disabled;
              break;
            }
            return !!value;
           }
        };
  
        /*
          all animations call this shared animation triggering function internally.
          The animationEvent variable refers to the JavaScript animation event that will be triggered
          and the className value is the name of the animation that will be applied within the
          CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
          and the onComplete callback will be fired once the animation is fully complete.
        */
        function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
  
          var runner = animationRunner(element, animationEvent, className);
          if(!runner) {
            fireDOMOperation();
            fireBeforeCallbackAsync();
            fireAfterCallbackAsync();
            closeAnimation();
            return;
          }
  
          className = runner.className;
          var elementEvents = angular.element._data(runner.node);
          elementEvents = elementEvents && elementEvents.events;
  
          if (!parentElement) {
            parentElement = afterElement ? afterElement.parent() : element.parent();
          }
  
          var ngAnimateState  = element.data(NG_ANIMATE_STATE) || {};
          var runningAnimations     = ngAnimateState.active || {};
          var totalActiveAnimations = ngAnimateState.totalActive || 0;
          var lastAnimation         = ngAnimateState.last;
  
          //only allow animations if the currently running animation is not structural
          //or if there is no animation running at all
          var skipAnimations = runner.isClassBased ?
            ngAnimateState.disabled || (lastAnimation && !lastAnimation.isClassBased) :
            false;
  
          //skip the animation if animations are disabled, a parent is already being animated,
          //the element is not currently attached to the document body or then completely close
          //the animation if any matching animations are not found at all.
          //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found.
          if (skipAnimations || animationsDisabled(element, parentElement)) {
            fireDOMOperation();
            fireBeforeCallbackAsync();
            fireAfterCallbackAsync();
            closeAnimation();
            return;
          }
  
          var skipAnimation = false;
          if(totalActiveAnimations > 0) {
            var animationsToCancel = [];
            if(!runner.isClassBased) {
              if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
                skipAnimation = true;
              } else {
                //cancel all animations when a structural animation takes place
                for(var klass in runningAnimations) {
                  animationsToCancel.push(runningAnimations[klass]);
                  cleanup(element, klass);
                }
                runningAnimations = {};
                totalActiveAnimations = 0;
              }
            } else if(lastAnimation.event == 'setClass') {
              animationsToCancel.push(lastAnimation);
              cleanup(element, className);
            }
            else if(runningAnimations[className]) {
              var current = runningAnimations[className];
              if(current.event == animationEvent) {
                skipAnimation = true;
              } else {
                animationsToCancel.push(current);
                cleanup(element, className);
              }
            }
  
            if(animationsToCancel.length > 0) {
              forEach(animationsToCancel, function(operation) {
                operation.cancel();
              });
            }
          }
  
          if(runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) {
            skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
          }
  
          if(skipAnimation) {
            fireBeforeCallbackAsync();
            fireAfterCallbackAsync();
            fireDoneCallbackAsync();
            return;
          }
  
          if(animationEvent == 'leave') {
            //there's no need to ever remove the listener since the element
            //will be removed (destroyed) after the leave animation ends or
            //is cancelled midway
            element.one('$destroy', function(e) {
              var element = angular.element(this);
              var state = element.data(NG_ANIMATE_STATE);
              if(state) {
                var activeLeaveAnimation = state.active['ng-leave'];
                if(activeLeaveAnimation) {
                  activeLeaveAnimation.cancel();
                  cleanup(element, 'ng-leave');
                }
              }
            });
          }
  
          //the ng-animate class does nothing, but it's here to allow for
          //parent animations to find and cancel child animations when needed
          element.addClass(NG_ANIMATE_CLASS_NAME);
  
          var localAnimationCount = globalAnimationCounter++;
          totalActiveAnimations++;
          runningAnimations[className] = runner;
  
          element.data(NG_ANIMATE_STATE, {
            last : runner,
            active : runningAnimations,
            index : localAnimationCount,
            totalActive : totalActiveAnimations
          });
  
          //first we run the before animations and when all of those are complete
          //then we perform the DOM operation and run the next set of animations
          fireBeforeCallbackAsync();
          runner.before(function(cancelled) {
            var data = element.data(NG_ANIMATE_STATE);
            cancelled = cancelled ||
                          !data || !data.active[className] ||
                          (runner.isClassBased && data.active[className].event != animationEvent);
  
            fireDOMOperation();
            if(cancelled === true) {
              closeAnimation();
            } else {
              fireAfterCallbackAsync();
              runner.after(closeAnimation);
            }
          });
  
          function fireDOMCallback(animationPhase) {
            var eventName = '$animate:' + animationPhase;
            if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) {
              $$asyncCallback(function() {
                element.triggerHandler(eventName, {
                  event : animationEvent,
                  className : className
                });
              });
            }
          }
  
          function fireBeforeCallbackAsync() {
            fireDOMCallback('before');
          }
  
          function fireAfterCallbackAsync() {
            fireDOMCallback('after');
          }
  
          function fireDoneCallbackAsync() {
            fireDOMCallback('close');
            if(doneCallback) {
              $$asyncCallback(function() {
                doneCallback();
              });
            }
          }
  
          //it is less complicated to use a flag than managing and canceling
          //timeouts containing multiple callbacks.
          function fireDOMOperation() {
            if(!fireDOMOperation.hasBeenRun) {
              fireDOMOperation.hasBeenRun = true;
              domOperation();
            }
          }
  
          function closeAnimation() {
            if(!closeAnimation.hasBeenRun) {
              closeAnimation.hasBeenRun = true;
              var data = element.data(NG_ANIMATE_STATE);
              if(data) {
                /* only structural animations wait for reflow before removing an
                   animation, but class-based animations don't. An example of this
                   failing would be when a parent HTML tag has a ng-class attribute
                   causing ALL directives below to skip animations during the digest */
                if(runner && runner.isClassBased) {
                  cleanup(element, className);
                } else {
                  $$asyncCallback(function() {
                    var data = element.data(NG_ANIMATE_STATE) || {};
                    if(localAnimationCount == data.index) {
                      cleanup(element, className, animationEvent);
                    }
                  });
                  element.data(NG_ANIMATE_STATE, data);
                }
              }
              fireDoneCallbackAsync();
            }
          }
        }
  
        function cancelChildAnimations(element) {
          var node = extractElementNode(element);
          if (node) {
            var nodes = angular.isFunction(node.getElementsByClassName) ?
              node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) :
              node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME);
            forEach(nodes, function(element) {
              element = angular.element(element);
              var data = element.data(NG_ANIMATE_STATE);
              if(data && data.active) {
                forEach(data.active, function(runner) {
                  runner.cancel();
                });
              }
            });
          }
        }
  
        function cleanup(element, className) {
          if(isMatchingElement(element, $rootElement)) {
            if(!rootAnimateState.disabled) {
              rootAnimateState.running = false;
              rootAnimateState.structural = false;
            }
          } else if(className) {
            var data = element.data(NG_ANIMATE_STATE) || {};
  
            var removeAnimations = className === true;
            if(!removeAnimations && data.active && data.active[className]) {
              data.totalActive--;
              delete data.active[className];
            }
  
            if(removeAnimations || !data.totalActive) {
              element.removeClass(NG_ANIMATE_CLASS_NAME);
              element.removeData(NG_ANIMATE_STATE);
            }
          }
        }
  
        function animationsDisabled(element, parentElement) {
          if (rootAnimateState.disabled) return true;
  
          if(isMatchingElement(element, $rootElement)) {
            return rootAnimateState.disabled || rootAnimateState.running;
          }
  
          do {
            //the element did not reach the root element which means that it
            //is not apart of the DOM. Therefore there is no reason to do
            //any animations on it
            if(parentElement.length === 0) break;
  
            var isRoot = isMatchingElement(parentElement, $rootElement);
            var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
            var result = state && (!!state.disabled || state.running || state.totalActive > 0);
            if(isRoot || result) {
              return result;
            }
  
            if(isRoot) return true;
          }
          while(parentElement = parentElement.parent());
  
          return true;
        }
      }]);
  
      $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
                             function($window,   $sniffer,   $timeout,   $$animateReflow) {
        // Detect proper transitionend/animationend event names.
        var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
  
        // If unprefixed events are not supported but webkit-prefixed are, use the latter.
        // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
        // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
        // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
        // Register both events in case `window.onanimationend` is not supported because of that,
        // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
        // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
        // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
        if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
          CSS_PREFIX = '-webkit-';
          TRANSITION_PROP = 'WebkitTransition';
          TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
        } else {
          TRANSITION_PROP = 'transition';
          TRANSITIONEND_EVENT = 'transitionend';
        }
  
        if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
          CSS_PREFIX = '-webkit-';
          ANIMATION_PROP = 'WebkitAnimation';
          ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
        } else {
          ANIMATION_PROP = 'animation';
          ANIMATIONEND_EVENT = 'animationend';
        }
  
        var DURATION_KEY = 'Duration';
        var PROPERTY_KEY = 'Property';
        var DELAY_KEY = 'Delay';
        var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
        var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
        var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
        var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions';
        var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
        var CLOSING_TIME_BUFFER = 1.5;
        var ONE_SECOND = 1000;
  
        var lookupCache = {};
        var parentCounter = 0;
        var animationReflowQueue = [];
        var cancelAnimationReflow;
        function afterReflow(element, callback) {
          if(cancelAnimationReflow) {
            cancelAnimationReflow();
          }
          animationReflowQueue.push(callback);
          cancelAnimationReflow = $$animateReflow(function() {
            forEach(animationReflowQueue, function(fn) {
              fn();
            });
  
            animationReflowQueue = [];
            cancelAnimationReflow = null;
            lookupCache = {};
          });
        }
  
        var closingTimer = null;
        var closingTimestamp = 0;
        var animationElementQueue = [];
        function animationCloseHandler(element, totalTime) {
          var node = extractElementNode(element);
          element = angular.element(node);
  
          //this item will be garbage collected by the closing
          //animation timeout
          animationElementQueue.push(element);
  
          //but it may not need to cancel out the existing timeout
          //if the timestamp is less than the previous one
          var futureTimestamp = Date.now() + (totalTime * 1000);
          if(futureTimestamp <= closingTimestamp) {
            return;
          }
  
          $timeout.cancel(closingTimer);
  
          closingTimestamp = futureTimestamp;
          closingTimer = $timeout(function() {
            closeAllAnimations(animationElementQueue);
            animationElementQueue = [];
          }, totalTime, false);
        }
  
        function closeAllAnimations(elements) {
          forEach(elements, function(element) {
            var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
            if(elementData) {
              (elementData.closeAnimationFn || noop)();
            }
          });
        }
  
        function getElementAnimationDetails(element, cacheKey) {
          var data = cacheKey ? lookupCache[cacheKey] : null;
          if(!data) {
            var transitionDuration = 0;
            var transitionDelay = 0;
            var animationDuration = 0;
            var animationDelay = 0;
            var transitionDelayStyle;
            var animationDelayStyle;
            var transitionDurationStyle;
            var transitionPropertyStyle;
  
            //we want all the styles defined before and after
            forEach(element, function(element) {
              if (element.nodeType == ELEMENT_NODE) {
                var elementStyles = $window.getComputedStyle(element) || {};
  
                transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
  
                transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
  
                transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
  
                transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
  
                transitionDelay  = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
  
                animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
  
                animationDelay   = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
  
                var aDuration  = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
  
                if(aDuration > 0) {
                  aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
                }
  
                animationDuration = Math.max(aDuration, animationDuration);
              }
            });
            data = {
              total : 0,
              transitionPropertyStyle: transitionPropertyStyle,
              transitionDurationStyle: transitionDurationStyle,
              transitionDelayStyle: transitionDelayStyle,
              transitionDelay: transitionDelay,
              transitionDuration: transitionDuration,
              animationDelayStyle: animationDelayStyle,
              animationDelay: animationDelay,
              animationDuration: animationDuration
            };
            if(cacheKey) {
              lookupCache[cacheKey] = data;
            }
          }
          return data;
        }
  
        function parseMaxTime(str) {
          var maxValue = 0;
          var values = angular.isString(str) ?
            str.split(/\s*,\s*/) :
            [];
          forEach(values, function(value) {
            maxValue = Math.max(parseFloat(value) || 0, maxValue);
          });
          return maxValue;
        }
  
        function getCacheKey(element) {
          var parentElement = element.parent();
          var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
          if(!parentID) {
            parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
            parentID = parentCounter;
          }
          return parentID + '-' + extractElementNode(element).className;
        }
  
        function animateSetup(animationEvent, element, className, calculationDecorator) {
          var cacheKey = getCacheKey(element);
          var eventCacheKey = cacheKey + ' ' + className;
          var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
  
          var stagger = {};
          if(itemIndex > 0) {
            var staggerClassName = className + '-stagger';
            var staggerCacheKey = cacheKey + ' ' + staggerClassName;
            var applyClasses = !lookupCache[staggerCacheKey];
  
            applyClasses && element.addClass(staggerClassName);
  
            stagger = getElementAnimationDetails(element, staggerCacheKey);
  
            applyClasses && element.removeClass(staggerClassName);
          }
  
          /* the animation itself may need to add/remove special CSS classes
           * before calculating the anmation styles */
          calculationDecorator = calculationDecorator ||
                                 function(fn) { return fn(); };
  
          element.addClass(className);
  
          var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
  
          var timings = calculationDecorator(function() {
            return getElementAnimationDetails(element, eventCacheKey);
          });
  
          var transitionDuration = timings.transitionDuration;
          var animationDuration = timings.animationDuration;
          if(transitionDuration === 0 && animationDuration === 0) {
            element.removeClass(className);
            return false;
          }
  
          element.data(NG_ANIMATE_CSS_DATA_KEY, {
            running : formerData.running || 0,
            itemIndex : itemIndex,
            stagger : stagger,
            timings : timings,
            closeAnimationFn : noop
          });
  
          //temporarily disable the transition so that the enter styles
          //don't animate twice (this is here to avoid a bug in Chrome/FF).
          var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass';
          if(transitionDuration > 0) {
            blockTransitions(element, className, isCurrentlyAnimating);
          }
  
          //staggering keyframe animations work by adjusting the `animation-delay` CSS property
          //on the given element, however, the delay value can only calculated after the reflow
          //since by that time $animate knows how many elements are being animated. Therefore,
          //until the reflow occurs the element needs to be blocked (where the keyframe animation
          //is set to `none 0s`). This blocking mechanism should only be set for when a stagger
          //animation is detected and when the element item index is greater than 0.
          if(animationDuration > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
            blockKeyframeAnimations(element);
          }
  
          return true;
        }
  
        function isStructuralAnimation(className) {
          return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave';
        }
  
        function blockTransitions(element, className, isAnimating) {
          if(isStructuralAnimation(className) || !isAnimating) {
            extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
          } else {
            element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME);
          }
        }
  
        function blockKeyframeAnimations(element) {
          extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
        }
  
        function unblockTransitions(element, className) {
          var prop = TRANSITION_PROP + PROPERTY_KEY;
          var node = extractElementNode(element);
          if(node.style[prop] && node.style[prop].length > 0) {
            node.style[prop] = '';
          }
          element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME);
        }
  
        function unblockKeyframeAnimations(element) {
          var prop = ANIMATION_PROP;
          var node = extractElementNode(element);
          if(node.style[prop] && node.style[prop].length > 0) {
            node.style[prop] = '';
          }
        }
  
        function animateRun(animationEvent, element, className, activeAnimationComplete) {
          var node = extractElementNode(element);
          var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
          if(node.className.indexOf(className) == -1 || !elementData) {
            activeAnimationComplete();
            return;
          }
  
          var activeClassName = '';
          forEach(className.split(' '), function(klass, i) {
            activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
          });
  
          var stagger = elementData.stagger;
          var timings = elementData.timings;
          var itemIndex = elementData.itemIndex;
          var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
          var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
          var maxDelayTime = maxDelay * ONE_SECOND;
  
          var startTime = Date.now();
          var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
  
          var style = '', appliedStyles = [];
          if(timings.transitionDuration > 0) {
            var propertyStyle = timings.transitionPropertyStyle;
            if(propertyStyle.indexOf('all') == -1) {
              style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';';
              style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';';
              appliedStyles.push(CSS_PREFIX + 'transition-property');
              appliedStyles.push(CSS_PREFIX + 'transition-duration');
            }
          }
  
          if(itemIndex > 0) {
            if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
              var delayStyle = timings.transitionDelayStyle;
              style += CSS_PREFIX + 'transition-delay: ' +
                       prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
              appliedStyles.push(CSS_PREFIX + 'transition-delay');
            }
  
            if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
              style += CSS_PREFIX + 'animation-delay: ' +
                       prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
              appliedStyles.push(CSS_PREFIX + 'animation-delay');
            }
          }
  
          if(appliedStyles.length > 0) {
            //the element being animated may sometimes contain comment nodes in
            //the jqLite object, so we're safe to use a single variable to house
            //the styles since there is always only one element being animated
            var oldStyle = node.getAttribute('style') || '';
            node.setAttribute('style', oldStyle + ' ' + style);
          }
  
          element.on(css3AnimationEvents, onAnimationProgress);
          element.addClass(activeClassName);
          elementData.closeAnimationFn = function() {
            onEnd();
            activeAnimationComplete();
          };
  
          var staggerTime       = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
          var animationTime     = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
          var totalTime         = (staggerTime + animationTime) * ONE_SECOND;
  
          elementData.running++;
          animationCloseHandler(element, totalTime);
          return onEnd;
  
          // This will automatically be called by $animate so
          // there is no need to attach this internally to the
          // timeout done method.
          function onEnd(cancelled) {
            element.off(css3AnimationEvents, onAnimationProgress);
            element.removeClass(activeClassName);
            animateClose(element, className);
            var node = extractElementNode(element);
            for (var i in appliedStyles) {
              node.style.removeProperty(appliedStyles[i]);
            }
          }
  
          function onAnimationProgress(event) {
            event.stopPropagation();
            var ev = event.originalEvent || event;
            var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
  
            /* Firefox (or possibly just Gecko) likes to not round values up
             * when a ms measurement is used for the animation */
            var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
  
            /* $manualTimeStamp is a mocked timeStamp value which is set
             * within browserTrigger(). This is only here so that tests can
             * mock animations properly. Real events fallback to event.timeStamp,
             * or, if they don't, then a timeStamp is automatically created for them.
             * We're checking to see if the timeStamp surpasses the expected delay,
             * but we're using elapsedTime instead of the timeStamp on the 2nd
             * pre-condition since animations sometimes close off early */
            if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
              activeAnimationComplete();
            }
          }
        }
  
        function prepareStaggerDelay(delayStyle, staggerDelay, index) {
          var style = '';
          forEach(delayStyle.split(','), function(val, i) {
            style += (i > 0 ? ',' : '') +
                     (index * staggerDelay + parseInt(val, 10)) + 's';
          });
          return style;
        }
  
        function animateBefore(animationEvent, element, className, calculationDecorator) {
          if(animateSetup(animationEvent, element, className, calculationDecorator)) {
            return function(cancelled) {
              cancelled && animateClose(element, className);
            };
          }
        }
  
        function animateAfter(animationEvent, element, className, afterAnimationComplete) {
          if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
            return animateRun(animationEvent, element, className, afterAnimationComplete);
          } else {
            animateClose(element, className);
            afterAnimationComplete();
          }
        }
  
        function animate(animationEvent, element, className, animationComplete) {
          //If the animateSetup function doesn't bother returning a
          //cancellation function then it means that there is no animation
          //to perform at all
          var preReflowCancellation = animateBefore(animationEvent, element, className);
          if(!preReflowCancellation) {
            animationComplete();
            return;
          }
  
          //There are two cancellation functions: one is before the first
          //reflow animation and the second is during the active state
          //animation. The first function will take care of removing the
          //data from the element which will not make the 2nd animation
          //happen in the first place
          var cancel = preReflowCancellation;
          afterReflow(element, function() {
            unblockTransitions(element, className);
            unblockKeyframeAnimations(element);
            //once the reflow is complete then we point cancel to
            //the new cancellation function which will remove all of the
            //animation properties from the active animation
            cancel = animateAfter(animationEvent, element, className, animationComplete);
          });
  
          return function(cancelled) {
            (cancel || noop)(cancelled);
          };
        }
  
        function animateClose(element, className) {
          element.removeClass(className);
          var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
          if(data) {
            if(data.running) {
              data.running--;
            }
            if(!data.running || data.running === 0) {
              element.removeData(NG_ANIMATE_CSS_DATA_KEY);
            }
          }
        }
  
        return {
          enter : function(element, animationCompleted) {
            return animate('enter', element, 'ng-enter', animationCompleted);
          },
  
          leave : function(element, animationCompleted) {
            return animate('leave', element, 'ng-leave', animationCompleted);
          },
  
          move : function(element, animationCompleted) {
            return animate('move', element, 'ng-move', animationCompleted);
          },
  
          beforeSetClass : function(element, add, remove, animationCompleted) {
            var className = suffixClasses(remove, '-remove') + ' ' +
                            suffixClasses(add, '-add');
            var cancellationMethod = animateBefore('setClass', element, className, function(fn) {
              /* when classes are removed from an element then the transition style
               * that is applied is the transition defined on the element without the
               * CSS class being there. This is how CSS3 functions outside of ngAnimate.
               * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
              var klass = element.attr('class');
              element.removeClass(remove);
              element.addClass(add);
              var timings = fn();
              element.attr('class', klass);
              return timings;
            });
  
            if(cancellationMethod) {
              afterReflow(element, function() {
                unblockTransitions(element, className);
                unblockKeyframeAnimations(element);
                animationCompleted();
              });
              return cancellationMethod;
            }
            animationCompleted();
          },
  
          beforeAddClass : function(element, className, animationCompleted) {
            var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) {
  
              /* when a CSS class is added to an element then the transition style that
               * is applied is the transition defined on the element when the CSS class
               * is added at the time of the animation. This is how CSS3 functions
               * outside of ngAnimate. */
              element.addClass(className);
              var timings = fn();
              element.removeClass(className);
              return timings;
            });
  
            if(cancellationMethod) {
              afterReflow(element, function() {
                unblockTransitions(element, className);
                unblockKeyframeAnimations(element);
                animationCompleted();
              });
              return cancellationMethod;
            }
            animationCompleted();
          },
  
          setClass : function(element, add, remove, animationCompleted) {
            remove = suffixClasses(remove, '-remove');
            add = suffixClasses(add, '-add');
            var className = remove + ' ' + add;
            return animateAfter('setClass', element, className, animationCompleted);
          },
  
          addClass : function(element, className, animationCompleted) {
            return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
          },
  
          beforeRemoveClass : function(element, className, animationCompleted) {
            var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) {
              /* when classes are removed from an element then the transition style
               * that is applied is the transition defined on the element without the
               * CSS class being there. This is how CSS3 functions outside of ngAnimate.
               * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
              var klass = element.attr('class');
              element.removeClass(className);
              var timings = fn();
              element.attr('class', klass);
              return timings;
            });
  
            if(cancellationMethod) {
              afterReflow(element, function() {
                unblockTransitions(element, className);
                unblockKeyframeAnimations(element);
                animationCompleted();
              });
              return cancellationMethod;
            }
            animationCompleted();
          },
  
          removeClass : function(element, className, animationCompleted) {
            return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
          }
        };
  
        function suffixClasses(classes, suffix) {
          var className = '';
          classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
          forEach(classes, function(klass, i) {
            if(klass && klass.length > 0) {
              className += (i > 0 ? ' ' : '') + klass + suffix;
            }
          });
          return className;
        }
      }]);
    }]);
  
  
  })(window, window.angular);
  
  /*!
   * angular-translate - v2.5.2 - 2014-12-10
   * http://github.com/angular-translate/angular-translate
   * Copyright (c) 2014 ; Licensed MIT
   */
  /**
   * @ngdoc overview
   * @name pascalprecht.translate
   *
   * @description
   * The main module which holds everything together.
   */
  angular.module('pascalprecht.translate', ['ng'])
  
  .run(['$translate', function ($translate) {
  
    var key = $translate.storageKey(),
        storage = $translate.storage();
  
    var fallbackFromIncorrectStorageValue = function() {
      var preferred = $translate.preferredLanguage();
      if (angular.isString(preferred)) {
        $translate.use(preferred);
        // $translate.use() will also remember the language.
        // So, we don't need to call storage.put() here.
      } else {
        storage.put(key, $translate.use());
      }
    };
  
    if (storage) {
      if (!storage.get(key)) {
        fallbackFromIncorrectStorageValue();
      } else {
        $translate.use(storage.get(key))['catch'](fallbackFromIncorrectStorageValue);
      }
    } else if (angular.isString($translate.preferredLanguage())) {
      $translate.use($translate.preferredLanguage());
    }
  }]);
  
  /**
   * @ngdoc object
   * @name pascalprecht.translate.$translateProvider
   * @description
   *
   * $translateProvider allows developers to register translation-tables, asynchronous loaders
   * and similar to configure translation behavior directly inside of a module.
   *
   */
  angular.module('pascalprecht.translate').provider('$translate', ['$STORAGE_KEY', function ($STORAGE_KEY) {
  
    var $translationTable = {},
        $preferredLanguage,
        $availableLanguageKeys = [],
        $languageKeyAliases,
        $fallbackLanguage,
        $fallbackWasString,
        $uses,
        $nextLang,
        $storageFactory,
        $storageKey = $STORAGE_KEY,
        $storagePrefix,
        $missingTranslationHandlerFactory,
        $interpolationFactory,
        $interpolatorFactories = [],
        $interpolationSanitizationStrategy = false,
        $loaderFactory,
        $cloakClassName = 'translate-cloak',
        $loaderOptions,
        $notFoundIndicatorLeft,
        $notFoundIndicatorRight,
        $postCompilingEnabled = false,
        NESTED_OBJECT_DELIMITER = '.',
        loaderCache;
  
    var version = '2.5.2';
  
    // tries to determine the browsers language
    var getFirstBrowserLanguage = function () {
      var nav = window.navigator,
          browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'],
          i,
          language;
  
      // support for HTML 5.1 "navigator.languages"
      if (angular.isArray(nav.languages)) {
        for (i = 0; i < nav.languages.length; i++) {
          language = nav.languages[i];
          if (language && language.length) {
            return language;
          }
        }
      }
  
      // support for other well known properties in browsers
      for (i = 0; i < browserLanguagePropertyKeys.length; i++) {
        language = nav[browserLanguagePropertyKeys[i]];
        if (language && language.length) {
          return language;
        }
      }
  
      return null;
    };
    getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage';
  
    // tries to determine the browsers locale
    var getLocale = function () {
      return (getFirstBrowserLanguage() || '').split('-').join('_');
    };
    getLocale.displayName = 'angular-translate/service: getLocale';
  
    /**
     * @name indexOf
     * @private
     *
     * @description
     * indexOf polyfill. Kinda sorta.
     *
     * @param {array} array Array to search in.
     * @param {string} searchElement Element to search for.
     *
     * @returns {int} Index of search element.
     */
    var indexOf = function(array, searchElement) {
      for (var i = 0, len = array.length; i < len; i++) {
        if (array[i] === searchElement) {
          return i;
        }
      }
      return -1;
    };
  
    /**
     * @name trim
     * @private
     *
     * @description
     * trim polyfill
     *
     * @returns {string} The string stripped of whitespace from both ends
     */
    var trim = function() {
      return this.replace(/^\s+|\s+$/g, '');
    };
  
    var negotiateLocale = function (preferred) {
  
      var avail = [],
          locale = angular.lowercase(preferred),
          i = 0,
          n = $availableLanguageKeys.length;
  
      for (; i < n; i++) {
        avail.push(angular.lowercase($availableLanguageKeys[i]));
      }
  
      if (indexOf(avail, locale) > -1) {
        return preferred;
      }
  
      if ($languageKeyAliases) {
        var alias;
        for (var langKeyAlias in $languageKeyAliases) {
          var hasWildcardKey = false;
          var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) &&
            angular.lowercase(langKeyAlias) === angular.lowercase(preferred);
  
          if (langKeyAlias.slice(-1) === '*') {
            hasWildcardKey = langKeyAlias.slice(0, -1) === preferred.slice(0, langKeyAlias.length-1);
          }
          if (hasExactKey || hasWildcardKey) {
            alias = $languageKeyAliases[langKeyAlias];
            if (indexOf(avail, angular.lowercase(alias)) > -1) {
              return alias;
            }
          }
        }
      }
  
      var parts = preferred.split('_');
  
      if (parts.length > 1 && indexOf(avail, angular.lowercase(parts[0])) > -1) {
        return parts[0];
      }
  
      // If everything fails, just return the preferred, unchanged.
      return preferred;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#translations
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Registers a new translation table for specific language key.
     *
     * To register a translation table for specific language, pass a defined language
     * key as first parameter.
     *
     * <pre>
     *  // register translation table for language: 'de_DE'
     *  $translateProvider.translations('de_DE', {
     *    'GREETING': 'Hallo Welt!'
     *  });
     *
     *  // register another one
     *  $translateProvider.translations('en_US', {
     *    'GREETING': 'Hello world!'
     *  });
     * </pre>
     *
     * When registering multiple translation tables for for the same language key,
     * the actual translation table gets extended. This allows you to define module
     * specific translation which only get added, once a specific module is loaded in
     * your app.
     *
     * Invoking this method with no arguments returns the translation table which was
     * registered with no language key. Invoking it with a language key returns the
     * related translation table.
     *
     * @param {string} key A language key.
     * @param {object} translationTable A plain old JavaScript object that represents a translation table.
     *
     */
    var translations = function (langKey, translationTable) {
  
      if (!langKey && !translationTable) {
        return $translationTable;
      }
  
      if (langKey && !translationTable) {
        if (angular.isString(langKey)) {
          return $translationTable[langKey];
        }
      } else {
        if (!angular.isObject($translationTable[langKey])) {
          $translationTable[langKey] = {};
        }
        angular.extend($translationTable[langKey], flatObject(translationTable));
      }
      return this;
    };
  
    this.translations = translations;
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#cloakClassName
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     *
     * Let's you change the class name for `translate-cloak` directive.
     * Default class name is `translate-cloak`.
     *
     * @param {string} name translate-cloak class name
     */
    this.cloakClassName = function (name) {
      if (!name) {
        return $cloakClassName;
      }
      $cloakClassName = name;
      return this;
    };
  
    /**
     * @name flatObject
     * @private
     *
     * @description
     * Flats an object. This function is used to flatten given translation data with
     * namespaces, so they are later accessible via dot notation.
     */
    var flatObject = function (data, path, result, prevKey) {
      var key, keyWithPath, keyWithShortPath, val;
  
      if (!path) {
        path = [];
      }
      if (!result) {
        result = {};
      }
      for (key in data) {
        if (!Object.prototype.hasOwnProperty.call(data, key)) {
          continue;
        }
        val = data[key];
        if (angular.isObject(val)) {
          flatObject(val, path.concat(key), result, key);
        } else {
          keyWithPath = path.length ? ('' + path.join(NESTED_OBJECT_DELIMITER) + NESTED_OBJECT_DELIMITER + key) : key;
          if(path.length && key === prevKey){
            // Create shortcut path (foo.bar == foo.bar.bar)
            keyWithShortPath = '' + path.join(NESTED_OBJECT_DELIMITER);
            // Link it to original path
            result[keyWithShortPath] = '@:' + keyWithPath;
          }
          result[keyWithPath] = val;
        }
      }
      return result;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#addInterpolation
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Adds interpolation services to angular-translate, so it can manage them.
     *
     * @param {object} factory Interpolation service factory
     */
    this.addInterpolation = function (factory) {
      $interpolatorFactories.push(factory);
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useMessageFormatInterpolation
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use interpolation functionality of messageformat.js.
     * This is useful when having high level pluralization and gender selection.
     */
    this.useMessageFormatInterpolation = function () {
      return this.useInterpolation('$translateMessageFormatInterpolation');
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useInterpolation
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate which interpolation style to use as default, application-wide.
     * Simply pass a factory/service name. The interpolation service has to implement
     * the correct interface.
     *
     * @param {string} factory Interpolation service name.
     */
    this.useInterpolation = function (factory) {
      $interpolationFactory = factory;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useSanitizeStrategy
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Simply sets a sanitation strategy type.
     *
     * @param {string} value Strategy type.
     */
    this.useSanitizeValueStrategy = function (value) {
      $interpolationSanitizationStrategy = value;
      return this;
    };
  
   /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#preferredLanguage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells the module which of the registered translation tables to use for translation
     * at initial startup by passing a language key. Similar to `$translateProvider#use`
     * only that it says which language to **prefer**.
     *
     * @param {string} langKey A language key.
     *
     */
    this.preferredLanguage = function(langKey) {
      setupPreferredLanguage(langKey);
      return this;
  
    };
    var setupPreferredLanguage = function (langKey) {
      if (langKey) {
        $preferredLanguage = langKey;
      }
      return $preferredLanguage;
    };
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicator
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Sets an indicator which is used when a translation isn't found. E.g. when
     * setting the indicator as 'X' and one tries to translate a translation id
     * called `NOT_FOUND`, this will result in `X NOT_FOUND X`.
     *
     * Internally this methods sets a left indicator and a right indicator using
     * `$translateProvider.translationNotFoundIndicatorLeft()` and
     * `$translateProvider.translationNotFoundIndicatorRight()`.
     *
     * **Note**: These methods automatically add a whitespace between the indicators
     * and the translation id.
     *
     * @param {string} indicator An indicator, could be any string.
     */
    this.translationNotFoundIndicator = function (indicator) {
      this.translationNotFoundIndicatorLeft(indicator);
      this.translationNotFoundIndicatorRight(indicator);
      return this;
    };
  
    /**
     * ngdoc function
     * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Sets an indicator which is used when a translation isn't found left to the
     * translation id.
     *
     * @param {string} indicator An indicator.
     */
    this.translationNotFoundIndicatorLeft = function (indicator) {
      if (!indicator) {
        return $notFoundIndicatorLeft;
      }
      $notFoundIndicatorLeft = indicator;
      return this;
    };
  
    /**
     * ngdoc function
     * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Sets an indicator which is used when a translation isn't found right to the
     * translation id.
     *
     * @param {string} indicator An indicator.
     */
    this.translationNotFoundIndicatorRight = function (indicator) {
      if (!indicator) {
        return $notFoundIndicatorRight;
      }
      $notFoundIndicatorRight = indicator;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#fallbackLanguage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells the module which of the registered translation tables to use when missing translations
     * at initial startup by passing a language key. Similar to `$translateProvider#use`
     * only that it says which language to **fallback**.
     *
     * @param {string||array} langKey A language key.
     *
     */
    this.fallbackLanguage = function (langKey) {
      fallbackStack(langKey);
      return this;
    };
  
    var fallbackStack = function (langKey) {
      if (langKey) {
        if (angular.isString(langKey)) {
          $fallbackWasString = true;
          $fallbackLanguage = [ langKey ];
        } else if (angular.isArray(langKey)) {
          $fallbackWasString = false;
          $fallbackLanguage = langKey;
        }
        if (angular.isString($preferredLanguage)  && indexOf($fallbackLanguage, $preferredLanguage) < 0) {
          $fallbackLanguage.push($preferredLanguage);
        }
  
        return this;
      } else {
        if ($fallbackWasString) {
          return $fallbackLanguage[0];
        } else {
          return $fallbackLanguage;
        }
      }
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#use
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Set which translation table to use for translation by given language key. When
     * trying to 'use' a language which isn't provided, it'll throw an error.
     *
     * You actually don't have to use this method since `$translateProvider#preferredLanguage`
     * does the job too.
     *
     * @param {string} langKey A language key.
     */
    this.use = function (langKey) {
      if (langKey) {
        if (!$translationTable[langKey] && (!$loaderFactory)) {
          // only throw an error, when not loading translation data asynchronously
          throw new Error("$translateProvider couldn't find translationTable for langKey: '" + langKey + "'");
        }
        $uses = langKey;
        return this;
      }
      return $uses;
    };
  
   /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#storageKey
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells the module which key must represent the choosed language by a user in the storage.
     *
     * @param {string} key A key for the storage.
     */
    var storageKey = function(key) {
      if (!key) {
        if ($storagePrefix) {
          return $storagePrefix + $storageKey;
        }
        return $storageKey;
      }
      $storageKey = key;
    };
  
    this.storageKey = storageKey;
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useUrlLoader
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use `$translateUrlLoader` extension service as loader.
     *
     * @param {string} url Url
     * @param {Object=} options Optional configuration object
     */
    this.useUrlLoader = function (url, options) {
      return this.useLoader('$translateUrlLoader', angular.extend({ url: url }, options));
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useStaticFilesLoader
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader.
     *
     * @param {Object=} options Optional configuration object
     */
    this.useStaticFilesLoader = function (options) {
      return this.useLoader('$translateStaticFilesLoader', options);
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useLoader
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use any other service as loader.
     *
     * @param {string} loaderFactory Factory name to use
     * @param {Object=} options Optional configuration object
     */
    this.useLoader = function (loaderFactory, options) {
      $loaderFactory = loaderFactory;
      $loaderOptions = options || {};
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useLocalStorage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use `$translateLocalStorage` service as storage layer.
     *
     */
    this.useLocalStorage = function () {
      return this.useStorage('$translateLocalStorage');
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useCookieStorage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use `$translateCookieStorage` service as storage layer.
     */
    this.useCookieStorage = function () {
      return this.useStorage('$translateCookieStorage');
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useStorage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use custom service as storage layer.
     */
    this.useStorage = function (storageFactory) {
      $storageFactory = storageFactory;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#storagePrefix
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Sets prefix for storage key.
     *
     * @param {string} prefix Storage key prefix
     */
    this.storagePrefix = function (prefix) {
      if (!prefix) {
        return prefix;
      }
      $storagePrefix = prefix;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandlerLog
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to use built-in log handler when trying to translate
     * a translation Id which doesn't exist.
     *
     * This is actually a shortcut method for `useMissingTranslationHandler()`.
     *
     */
    this.useMissingTranslationHandlerLog = function () {
      return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog');
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandler
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Expects a factory name which later gets instantiated with `$injector`.
     * This method can be used to tell angular-translate to use a custom
     * missingTranslationHandler. Just build a factory which returns a function
     * and expects a translation id as argument.
     *
     * Example:
     * <pre>
     *  app.config(function ($translateProvider) {
     *    $translateProvider.useMissingTranslationHandler('customHandler');
     *  });
     *
     *  app.factory('customHandler', function (dep1, dep2) {
     *    return function (translationId) {
     *      // something with translationId and dep1 and dep2
     *    };
     *  });
     * </pre>
     *
     * @param {string} factory Factory name
     */
    this.useMissingTranslationHandler = function (factory) {
      $missingTranslationHandlerFactory = factory;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#usePostCompiling
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * If post compiling is enabled, all translated values will be processed
     * again with AngularJS' $compile.
     *
     * Example:
     * <pre>
     *  app.config(function ($translateProvider) {
     *    $translateProvider.usePostCompiling(true);
     *  });
     * </pre>
     *
     * @param {string} factory Factory name
     */
    this.usePostCompiling = function (value) {
      $postCompilingEnabled = !(!value);
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#determinePreferredLanguage
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Tells angular-translate to try to determine on its own which language key
     * to set as preferred language. When `fn` is given, angular-translate uses it
     * to determine a language key, otherwise it uses the built-in `getLocale()`
     * method.
     *
     * The `getLocale()` returns a language key in the format `[lang]_[country]` or
     * `[lang]` depending on what the browser provides.
     *
     * Use this method at your own risk, since not all browsers return a valid
     * locale.
     *
     * @param {object=} fn Function to determine a browser's locale
     */
    this.determinePreferredLanguage = function (fn) {
  
      var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale();
  
      if (!$availableLanguageKeys.length) {
        $preferredLanguage = locale;
      } else {
        $preferredLanguage = negotiateLocale(locale);
      }
  
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#registerAvailableLanguageKeys
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Registers a set of language keys the app will work with. Use this method in
     * combination with
     * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
     * When available languages keys are registered, angular-translate
     * tries to find the best fitting language key depending on the browsers locale,
     * considering your language key convention.
     *
     * @param {object} languageKeys Array of language keys the your app will use
     * @param {object=} aliases Alias map.
     */
    this.registerAvailableLanguageKeys = function (languageKeys, aliases) {
      if (languageKeys) {
        $availableLanguageKeys = languageKeys;
        if (aliases) {
          $languageKeyAliases = aliases;
        }
        return this;
      }
      return $availableLanguageKeys;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateProvider#useLoaderCache
     * @methodOf pascalprecht.translate.$translateProvider
     *
     * @description
     * Registers a cache for internal $http based loaders.
     * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
     * When false the cache will be disabled (default). When true or undefined
     * the cache will be a default (see $cacheFactory). When an object it will
     * be treat as a cache object itself: the usage is $http({cache: cache})
     *
     * @param {object} cache boolean, string or cache-object
     */
    this.useLoaderCache = function (cache) {
      if (cache === false) {
        // disable cache
        loaderCache = undefined;
      } else if (cache === true) {
        // enable cache using AJS defaults
        loaderCache = true;
      } else if (typeof(cache) === 'undefined') {
        // enable cache using default
        loaderCache = '$translationCache';
      } else if (cache) {
        // enable cache using given one (see $cacheFactory)
        loaderCache = cache;
      }
      return this;
    };
  
    /**
     * @ngdoc object
     * @name pascalprecht.translate.$translate
     * @requires $interpolate
     * @requires $log
     * @requires $rootScope
     * @requires $q
     *
     * @description
     * The `$translate` service is the actual core of angular-translate. It expects a translation id
     * and optional interpolate parameters to translate contents.
     *
     * <pre>
     *  $translate('HEADLINE_TEXT').then(function (translation) {
     *    $scope.translatedText = translation;
     *  });
     * </pre>
     *
     * @param {string|array} translationId A token which represents a translation id
     *                                     This can be optionally an array of translation ids which
     *                                     results that the function returns an object where each key
     *                                     is the translation id and the value the translation.
     * @param {object=} interpolateParams An object hash for dynamic values
     * @param {string} interpolationId The id of the interpolation to use
     * @returns {object} promise
     */
    this.$get = [
      '$log',
      '$injector',
      '$rootScope',
      '$q',
      function ($log, $injector, $rootScope, $q) {
  
        var Storage,
            defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),
            pendingLoader = false,
            interpolatorHashMap = {},
            langPromises = {},
            fallbackIndex,
            startFallbackIteration;
  
        var $translate = function (translationId, interpolateParams, interpolationId) {
  
          // Duck detection: If the first argument is an array, a bunch of translations was requested.
          // The result is an object.
          if (angular.isArray(translationId)) {
            // Inspired by Q.allSettled by Kris Kowal
            // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563
            // This transforms all promises regardless resolved or rejected
            var translateAll = function (translationIds) {
              var results = {}; // storing the actual results
              var promises = []; // promises to wait for
              // Wraps the promise a) being always resolved and b) storing the link id->value
              var translate = function (translationId) {
                var deferred = $q.defer();
                var regardless = function (value) {
                  results[translationId] = value;
                  deferred.resolve([translationId, value]);
                };
                // we don't care whether the promise was resolved or rejected; just store the values
                $translate(translationId, interpolateParams, interpolationId).then(regardless, regardless);
                return deferred.promise;
              };
              for (var i = 0, c = translationIds.length; i < c; i++) {
                promises.push(translate(translationIds[i]));
              }
              // wait for all (including storing to results)
              return $q.all(promises).then(function () {
                // return the results
                return results;
              });
            };
            return translateAll(translationId);
          }
  
          var deferred = $q.defer();
  
          // trim off any whitespace
          if (translationId) {
            translationId = trim.apply(translationId);
          }
  
          var promiseToWaitFor = (function () {
            var promise = $preferredLanguage ?
              langPromises[$preferredLanguage] :
              langPromises[$uses];
  
            fallbackIndex = 0;
  
            if ($storageFactory && !promise) {
              // looks like there's no pending promise for $preferredLanguage or
              // $uses. Maybe there's one pending for a language that comes from
              // storage.
              var langKey = Storage.get($storageKey);
              promise = langPromises[langKey];
  
              if ($fallbackLanguage && $fallbackLanguage.length) {
                  var index = indexOf($fallbackLanguage, langKey);
                  // maybe the language from storage is also defined as fallback language
                  // we increase the fallback language index to not search in that language
                  // as fallback, since it's probably the first used language
                  // in that case the index starts after the first element
                  fallbackIndex = (index === 0) ? 1 : 0;
  
                  // but we can make sure to ALWAYS fallback to preferred language at least
                  if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {
                    $fallbackLanguage.push($preferredLanguage);
                  }
              }
            }
            return promise;
          }());
  
          if (!promiseToWaitFor) {
            // no promise to wait for? okay. Then there's no loader registered
            // nor is a one pending for language that comes from storage.
            // We can just translate.
            determineTranslation(translationId, interpolateParams, interpolationId).then(deferred.resolve, deferred.reject);
          } else {
            promiseToWaitFor.then(function () {
              determineTranslation(translationId, interpolateParams, interpolationId).then(deferred.resolve, deferred.reject);
            }, deferred.reject);
          }
          return deferred.promise;
        };
  
        /**
         * @name applyNotFoundIndicators
         * @private
         *
         * @description
         * Applies not fount indicators to given translation id, if needed.
         * This function gets only executed, if a translation id doesn't exist,
         * which is why a translation id is expected as argument.
         *
         * @param {string} translationId Translation id.
         * @returns {string} Same as given translation id but applied with not found
         * indicators.
         */
        var applyNotFoundIndicators = function (translationId) {
          // applying notFoundIndicators
          if ($notFoundIndicatorLeft) {
            translationId = [$notFoundIndicatorLeft, translationId].join(' ');
          }
          if ($notFoundIndicatorRight) {
            translationId = [translationId, $notFoundIndicatorRight].join(' ');
          }
          return translationId;
        };
  
        /**
         * @name useLanguage
         * @private
         *
         * @description
         * Makes actual use of a language by setting a given language key as used
         * language and informs registered interpolators to also use the given
         * key as locale.
         *
         * @param {key} Locale key.
         */
        var useLanguage = function (key) {
          $uses = key;
          $rootScope.$emit('$translateChangeSuccess', {language: key});
  
          if ($storageFactory) {
            Storage.put($translate.storageKey(), $uses);
          }
          // inform default interpolator
          defaultInterpolator.setLocale($uses);
          // inform all others too!
          angular.forEach(interpolatorHashMap, function (interpolator, id) {
            interpolatorHashMap[id].setLocale($uses);
          });
          $rootScope.$emit('$translateChangeEnd', {language: key});
        };
  
        /**
         * @name loadAsync
         * @private
         *
         * @description
         * Kicks of registered async loader using `$injector` and applies existing
         * loader options. When resolved, it updates translation tables accordingly
         * or rejects with given language key.
         *
         * @param {string} key Language key.
         * @return {Promise} A promise.
         */
        var loadAsync = function (key) {
          if (!key) {
            throw 'No language key specified for loading.';
          }
  
          var deferred = $q.defer();
  
          $rootScope.$emit('$translateLoadingStart', {language: key});
          pendingLoader = true;
  
          var cache = loaderCache;
          if (typeof(cache) === 'string') {
            // getting on-demand instance of loader
            cache = $injector.get(cache);
          }
  
          var loaderOptions = angular.extend({}, $loaderOptions, {
            key: key,
            $http: angular.extend({}, {
              cache: cache
            }, $loaderOptions.$http)
          });
  
          $injector.get($loaderFactory)(loaderOptions).then(function (data) {
            var translationTable = {};
            $rootScope.$emit('$translateLoadingSuccess', {language: key});
  
            if (angular.isArray(data)) {
              angular.forEach(data, function (table) {
                angular.extend(translationTable, flatObject(table));
              });
            } else {
              angular.extend(translationTable, flatObject(data));
            }
            pendingLoader = false;
            deferred.resolve({
              key: key,
              table: translationTable
            });
            $rootScope.$emit('$translateLoadingEnd', {language: key});
          }, function (key) {
            $rootScope.$emit('$translateLoadingError', {language: key});
            deferred.reject(key);
            $rootScope.$emit('$translateLoadingEnd', {language: key});
          });
          return deferred.promise;
        };
  
        if ($storageFactory) {
          Storage = $injector.get($storageFactory);
  
          if (!Storage.get || !Storage.put) {
            throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');
          }
        }
  
        // apply additional settings
        if (angular.isFunction(defaultInterpolator.useSanitizeValueStrategy)) {
          defaultInterpolator.useSanitizeValueStrategy($interpolationSanitizationStrategy);
        }
  
        // if we have additional interpolations that were added via
        // $translateProvider.addInterpolation(), we have to map'em
        if ($interpolatorFactories.length) {
          angular.forEach($interpolatorFactories, function (interpolatorFactory) {
            var interpolator = $injector.get(interpolatorFactory);
            // setting initial locale for each interpolation service
            interpolator.setLocale($preferredLanguage || $uses);
            // apply additional settings
            if (angular.isFunction(interpolator.useSanitizeValueStrategy)) {
              interpolator.useSanitizeValueStrategy($interpolationSanitizationStrategy);
            }
            // make'em recognizable through id
            interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;
          });
        }
  
        /**
         * @name getTranslationTable
         * @private
         *
         * @description
         * Returns a promise that resolves to the translation table
         * or is rejected if an error occurred.
         *
         * @param langKey
         * @returns {Q.promise}
         */
        var getTranslationTable = function (langKey) {
          var deferred = $q.defer();
          if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {
            deferred.resolve($translationTable[langKey]);
          } else if (langPromises[langKey]) {
            langPromises[langKey].then(function (data) {
              translations(data.key, data.table);
              deferred.resolve(data.table);
            }, deferred.reject);
          } else {
            deferred.reject();
          }
          return deferred.promise;
        };
  
        /**
         * @name getFallbackTranslation
         * @private
         *
         * @description
         * Returns a promise that will resolve to the translation
         * or be rejected if no translation was found for the language.
         * This function is currently only used for fallback language translation.
         *
         * @param langKey The language to translate to.
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {Q.promise}
         */
        var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator) {
          var deferred = $q.defer();
  
          getTranslationTable(langKey).then(function (translationTable) {
            if (Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
              Interpolator.setLocale(langKey);
              deferred.resolve(Interpolator.interpolate(translationTable[translationId], interpolateParams));
              Interpolator.setLocale($uses);
            } else {
              deferred.reject();
            }
          }, deferred.reject);
  
          return deferred.promise;
        };
  
        /**
         * @name getFallbackTranslationInstant
         * @private
         *
         * @description
         * Returns a translation
         * This function is currently only used for fallback language translation.
         *
         * @param langKey The language to translate to.
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {string} translation
         */
        var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator) {
          var result, translationTable = $translationTable[langKey];
  
          if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
            Interpolator.setLocale(langKey);
            result = Interpolator.interpolate(translationTable[translationId], interpolateParams);
            Interpolator.setLocale($uses);
          }
  
          return result;
        };
  
  
        /**
         * @name translateByHandler
         * @private
         *
         * Translate by missing translation handler.
         *
         * @param translationId
         * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
         * absent
         */
        var translateByHandler = function (translationId) {
          // If we have a handler factory - we might also call it here to determine if it provides
          // a default text for a translationid that can't be found anywhere in our tables
          if ($missingTranslationHandlerFactory) {
            var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses);
            if (resultString !== undefined) {
              return resultString;
            } else {
              return translationId;
            }
          } else {
            return translationId;
          }
        };
  
        /**
         * @name resolveForFallbackLanguage
         * @private
         *
         * Recursive helper function for fallbackTranslation that will sequentially look
         * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
         *
         * @param fallbackLanguageIndex
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {Q.promise} Promise that will resolve to the translation.
         */
        var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {
          var deferred = $q.defer();
  
          if (fallbackLanguageIndex < $fallbackLanguage.length) {
            var langKey = $fallbackLanguage[fallbackLanguageIndex];
            getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator).then(
              deferred.resolve,
              function () {
                // Look in the next fallback language for a translation.
                // It delays the resolving by passing another promise to resolve.
                resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator).then(deferred.resolve);
              }
            );
          } else {
            // No translation found in any fallback language
            deferred.resolve(translateByHandler(translationId));
          }
          return deferred.promise;
        };
  
        /**
         * @name resolveForFallbackLanguageInstant
         * @private
         *
         * Recursive helper function for fallbackTranslation that will sequentially look
         * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
         *
         * @param fallbackLanguageIndex
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {string} translation
         */
        var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {
          var result;
  
          if (fallbackLanguageIndex < $fallbackLanguage.length) {
            var langKey = $fallbackLanguage[fallbackLanguageIndex];
            result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator);
            if (!result) {
              result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
            }
          }
          return result;
        };
  
        /**
         * Translates with the usage of the fallback languages.
         *
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {Q.promise} Promise, that resolves to the translation.
         */
        var fallbackTranslation = function (translationId, interpolateParams, Interpolator) {
          // Start with the fallbackLanguage with index 0
          return resolveForFallbackLanguage((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);
        };
  
        /**
         * Translates with the usage of the fallback languages.
         *
         * @param translationId
         * @param interpolateParams
         * @param Interpolator
         * @returns {String} translation
         */
        var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator) {
          // Start with the fallbackLanguage with index 0
          return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);
        };
  
        var determineTranslation = function (translationId, interpolateParams, interpolationId) {
  
          var deferred = $q.defer();
  
          var table = $uses ? $translationTable[$uses] : $translationTable,
              Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
  
          // if the translation id exists, we can just interpolate it
          if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
            var translation = table[translationId];
  
            // If using link, rerun $translate with linked translationId and return it
            if (translation.substr(0, 2) === '@:') {
  
              $translate(translation.substr(2), interpolateParams, interpolationId)
                .then(deferred.resolve, deferred.reject);
            } else {
              deferred.resolve(Interpolator.interpolate(translation, interpolateParams));
            }
          } else {
            var missingTranslationHandlerTranslation;
            // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
            if ($missingTranslationHandlerFactory && !pendingLoader) {
              missingTranslationHandlerTranslation = translateByHandler(translationId);
            }
  
            // since we couldn't translate the inital requested translation id,
            // we try it now with one or more fallback languages, if fallback language(s) is
            // configured.
            if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
              fallbackTranslation(translationId, interpolateParams, Interpolator)
                  .then(function (translation) {
                    deferred.resolve(translation);
                  }, function (_translationId) {
                    deferred.reject(applyNotFoundIndicators(_translationId));
                  });
            } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
              // looks like the requested translation id doesn't exists.
              // Now, if there is a registered handler for missing translations and no
              // asyncLoader is pending, we execute the handler
              deferred.resolve(missingTranslationHandlerTranslation);
            } else {
              deferred.reject(applyNotFoundIndicators(translationId));
            }
          }
          return deferred.promise;
        };
  
        var determineTranslationInstant = function (translationId, interpolateParams, interpolationId) {
  
          var result, table = $uses ? $translationTable[$uses] : $translationTable,
              Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
  
          // if the translation id exists, we can just interpolate it
          if (table && Object.prototype.hasOwnProperty.call(table, translationId)) {
            var translation = table[translationId];
  
            // If using link, rerun $translate with linked translationId and return it
            if (translation.substr(0, 2) === '@:') {
              result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId);
            } else {
              result = Interpolator.interpolate(translation, interpolateParams);
            }
          } else {
            var missingTranslationHandlerTranslation;
            // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
            if ($missingTranslationHandlerFactory && !pendingLoader) {
              missingTranslationHandlerTranslation = translateByHandler(translationId);
            }
  
            // since we couldn't translate the inital requested translation id,
            // we try it now with one or more fallback languages, if fallback language(s) is
            // configured.
            if ($uses && $fallbackLanguage && $fallbackLanguage.length) {
              fallbackIndex = 0;
              result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);
            } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
              // looks like the requested translation id doesn't exists.
              // Now, if there is a registered handler for missing translations and no
              // asyncLoader is pending, we execute the handler
              result = missingTranslationHandlerTranslation;
            } else {
              result = applyNotFoundIndicators(translationId);
            }
          }
  
          return result;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#preferredLanguage
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the language key for the preferred language.
         *
         * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
         *
         * @return {string} preferred language key
         */
        $translate.preferredLanguage = function (langKey) {
          if(langKey) {
            setupPreferredLanguage(langKey);
          }
          return $preferredLanguage;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#cloakClassName
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the configured class name for `translate-cloak` directive.
         *
         * @return {string} cloakClassName
         */
        $translate.cloakClassName = function () {
          return $cloakClassName;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#fallbackLanguage
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the language key for the fallback languages or sets a new fallback stack.
         *
         * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
         *
         * @return {string||array} fallback language key
         */
        $translate.fallbackLanguage = function (langKey) {
          if (langKey !== undefined && langKey !== null) {
            fallbackStack(langKey);
  
            // as we might have an async loader initiated and a new translation language might have been defined
            // we need to add the promise to the stack also. So - iterate.
            if ($loaderFactory) {
              if ($fallbackLanguage && $fallbackLanguage.length) {
                for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
                  if (!langPromises[$fallbackLanguage[i]]) {
                    langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
                  }
                }
              }
            }
            $translate.use($translate.use());
          }
          if ($fallbackWasString) {
            return $fallbackLanguage[0];
          } else {
            return $fallbackLanguage;
          }
  
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#useFallbackLanguage
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Sets the first key of the fallback language stack to be used for translation.
         * Therefore all languages in the fallback array BEFORE this key will be skipped!
         *
         * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
         * get back to the whole stack
         */
        $translate.useFallbackLanguage = function (langKey) {
          if (langKey !== undefined && langKey !== null) {
            if (!langKey) {
              startFallbackIteration = 0;
            } else {
              var langKeyPosition = indexOf($fallbackLanguage, langKey);
              if (langKeyPosition > -1) {
                startFallbackIteration = langKeyPosition;
              }
            }
  
          }
  
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#proposedLanguage
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the language key of language that is currently loaded asynchronously.
         *
         * @return {string} language key
         */
        $translate.proposedLanguage = function () {
          return $nextLang;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#storage
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns registered storage.
         *
         * @return {object} Storage
         */
        $translate.storage = function () {
          return Storage;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#use
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Tells angular-translate which language to use by given language key. This method is
         * used to change language at runtime. It also takes care of storing the language
         * key in a configured store to let your app remember the choosed language.
         *
         * When trying to 'use' a language which isn't available it tries to load it
         * asynchronously with registered loaders.
         *
         * Returns promise object with loaded language file data
         * @example
         * $translate.use("en_US").then(function(data){
         *   $scope.text = $translate("HELLO");
         * });
         *
         * @param {string} key Language key
         * @return {string} Language key
         */
        $translate.use = function (key) {
          if (!key) {
            return $uses;
          }
  
          var deferred = $q.defer();
  
          $rootScope.$emit('$translateChangeStart', {language: key});
  
          // Try to get the aliased language key
          var aliasedKey = negotiateLocale(key);
          if (aliasedKey) {
            key = aliasedKey;
          }
  
          // if there isn't a translation table for the language we've requested,
          // we load it asynchronously
          if (!$translationTable[key] && $loaderFactory && !langPromises[key]) {
            $nextLang = key;
            langPromises[key] = loadAsync(key).then(function (translation) {
              translations(translation.key, translation.table);
              deferred.resolve(translation.key);
  
              useLanguage(translation.key);
              if ($nextLang === key) {
                $nextLang = undefined;
              }
              return translation;
            }, function (key) {
              if ($nextLang === key) {
                $nextLang = undefined;
              }
              $rootScope.$emit('$translateChangeError', {language: key});
              deferred.reject(key);
              $rootScope.$emit('$translateChangeEnd', {language: key});
            });
          } else {
            deferred.resolve(key);
            useLanguage(key);
          }
  
          return deferred.promise;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#storageKey
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the key for the storage.
         *
         * @return {string} storage key
         */
        $translate.storageKey = function () {
          return storageKey();
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#isPostCompilingEnabled
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns whether post compiling is enabled or not
         *
         * @return {bool} storage key
         */
        $translate.isPostCompilingEnabled = function () {
          return $postCompilingEnabled;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#refresh
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
         * the module will drop all existent translation tables and load new version of those which
         * are currently in use.
         *
         * Refresh means that the module will drop target translation table and try to load it again.
         *
         * In case there are no loaders registered the refresh() method will throw an Error.
         *
         * If the module is able to refresh translation tables refresh() method will broadcast
         * $translateRefreshStart and $translateRefreshEnd events.
         *
         * @example
         * // this will drop all currently existent translation tables and reload those which are
         * // currently in use
         * $translate.refresh();
         * // this will refresh a translation table for the en_US language
         * $translate.refresh('en_US');
         *
         * @param {string} langKey A language key of the table, which has to be refreshed
         *
         * @return {promise} Promise, which will be resolved in case a translation tables refreshing
         * process is finished successfully, and reject if not.
         */
        $translate.refresh = function (langKey) {
          if (!$loaderFactory) {
            throw new Error('Couldn\'t refresh translation table, no loader registered!');
          }
  
          var deferred = $q.defer();
  
          function resolve() {
            deferred.resolve();
            $rootScope.$emit('$translateRefreshEnd', {language: langKey});
          }
  
          function reject() {
            deferred.reject();
            $rootScope.$emit('$translateRefreshEnd', {language: langKey});
          }
  
          $rootScope.$emit('$translateRefreshStart', {language: langKey});
  
          if (!langKey) {
            // if there's no language key specified we refresh ALL THE THINGS!
            var tables = [], loadingKeys = {};
  
            // reload registered fallback languages
            if ($fallbackLanguage && $fallbackLanguage.length) {
              for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
                tables.push(loadAsync($fallbackLanguage[i]));
                loadingKeys[$fallbackLanguage[i]] = true;
              }
            }
  
            // reload currently used language
            if ($uses && !loadingKeys[$uses]) {
              tables.push(loadAsync($uses));
            }
  
            $q.all(tables).then(function (tableData) {
              angular.forEach(tableData, function (data) {
                if ($translationTable[data.key]) {
                  delete $translationTable[data.key];
                }
                translations(data.key, data.table);
              });
              if ($uses) {
                useLanguage($uses);
              }
              resolve();
            });
  
          } else if ($translationTable[langKey]) {
  
            loadAsync(langKey).then(function (data) {
              translations(data.key, data.table);
              if (langKey === $uses) {
                useLanguage($uses);
              }
              resolve();
            }, reject);
  
          } else {
            reject();
          }
          return deferred.promise;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#instant
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns a translation instantly from the internal state of loaded translation. All rules
         * regarding the current language, the preferred language of even fallback languages will be
         * used except any promise handling. If a language was not found, an asynchronous loading
         * will be invoked in the background.
         *
         * @param {string|array} translationId A token which represents a translation id
         *                                     This can be optionally an array of translation ids which
         *                                     results that the function's promise returns an object where
         *                                     each key is the translation id and the value the translation.
         * @param {object} interpolateParams Params
         * @param {string} interpolationId The id of the interpolation to use
         *
         * @return {string} translation
         */
        $translate.instant = function (translationId, interpolateParams, interpolationId) {
  
          // Detect undefined and null values to shorten the execution and prevent exceptions
          if (translationId === null || angular.isUndefined(translationId)) {
            return translationId;
          }
  
          // Duck detection: If the first argument is an array, a bunch of translations was requested.
          // The result is an object.
          if (angular.isArray(translationId)) {
            var results = {};
            for (var i = 0, c = translationId.length; i < c; i++) {
              results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId);
            }
            return results;
          }
  
          // We discarded unacceptable values. So we just need to verify if translationId is empty String
          if (angular.isString(translationId) && translationId.length < 1) {
            return translationId;
          }
  
          // trim off any whitespace
          if (translationId) {
            translationId = trim.apply(translationId);
          }
  
          var result, possibleLangKeys = [];
          if ($preferredLanguage) {
            possibleLangKeys.push($preferredLanguage);
          }
          if ($uses) {
            possibleLangKeys.push($uses);
          }
          if ($fallbackLanguage && $fallbackLanguage.length) {
            possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
          }
          for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
            var possibleLangKey = possibleLangKeys[j];
            if ($translationTable[possibleLangKey]) {
              if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
                result = determineTranslationInstant(translationId, interpolateParams, interpolationId);
              }
            }
            if (typeof result !== 'undefined') {
              break;
            }
          }
  
          if (!result && result !== '') {
            // Return translation of default interpolator if not found anything.
            result = defaultInterpolator.interpolate(translationId, interpolateParams);
            if ($missingTranslationHandlerFactory && !pendingLoader) {
              result = translateByHandler(translationId);
            }
          }
  
          return result;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#versionInfo
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the current version information for the angular-translate library
         *
         * @return {string} angular-translate version
         */
        $translate.versionInfo = function () {
          return version;
        };
  
        /**
         * @ngdoc function
         * @name pascalprecht.translate.$translate#loaderCache
         * @methodOf pascalprecht.translate.$translate
         *
         * @description
         * Returns the defined loaderCache.
         *
         * @return {boolean|string|object} current value of loaderCache
         */
        $translate.loaderCache = function () {
          return loaderCache;
        };
  
        if ($loaderFactory) {
  
          // If at least one async loader is defined and there are no
          // (default) translations available we should try to load them.
          if (angular.equals($translationTable, {})) {
            $translate.use($translate.use());
          }
  
          // Also, if there are any fallback language registered, we start
          // loading them asynchronously as soon as we can.
          if ($fallbackLanguage && $fallbackLanguage.length) {
            var processAsyncResult = function (translation) {
              translations(translation.key, translation.table);
              $rootScope.$emit('$translateChangeEnd', { language: translation.key });
              return translation;
            };
            for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
              langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]).then(processAsyncResult);
            }
          }
        }
  
        return $translate;
      }
    ];
  }]);
  
  /**
   * @ngdoc object
   * @name pascalprecht.translate.$translateDefaultInterpolation
   * @requires $interpolate
   *
   * @description
   * Uses angular's `$interpolate` services to interpolate strings against some values.
   *
   * @return {object} $translateInterpolator Interpolator service
   */
  angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', ['$interpolate', function ($interpolate) {
  
    var $translateInterpolator = {},
        $locale,
        $identifier = 'default',
        $sanitizeValueStrategy = null,
        // map of all sanitize strategies
        sanitizeValueStrategies = {
          escaped: function (params) {
            var result = {};
            for (var key in params) {
              if (Object.prototype.hasOwnProperty.call(params, key)) {
                result[key] = angular.element('<div></div>').text(params[key]).html();
              }
            }
            return result;
          }
        };
  
    var sanitizeParams = function (params) {
      var result;
      if (angular.isFunction(sanitizeValueStrategies[$sanitizeValueStrategy])) {
        result = sanitizeValueStrategies[$sanitizeValueStrategy](params);
      } else {
        result = params;
      }
      return result;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale
     * @methodOf pascalprecht.translate.$translateDefaultInterpolation
     *
     * @description
     * Sets current locale (this is currently not use in this interpolation).
     *
     * @param {string} locale Language key or locale.
     */
    $translateInterpolator.setLocale = function (locale) {
      $locale = locale;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier
     * @methodOf pascalprecht.translate.$translateDefaultInterpolation
     *
     * @description
     * Returns an identifier for this interpolation service.
     *
     * @returns {string} $identifier
     */
    $translateInterpolator.getInterpolationIdentifier = function () {
      return $identifier;
    };
  
    $translateInterpolator.useSanitizeValueStrategy = function (value) {
      $sanitizeValueStrategy = value;
      return this;
    };
  
    /**
     * @ngdoc function
     * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate
     * @methodOf pascalprecht.translate.$translateDefaultInterpolation
     *
     * @description
     * Interpolates given string agains given interpolate params using angulars
     * `$interpolate` service.
     *
     * @returns {string} interpolated string.
     */
    $translateInterpolator.interpolate = function (string, interpolateParams) {
      if ($sanitizeValueStrategy) {
        interpolateParams = sanitizeParams(interpolateParams);
      }
      return $interpolate(string)(interpolateParams || {});
    };
  
    return $translateInterpolator;
  }]);
  
  angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY');
  
  angular.module('pascalprecht.translate')
  /**
   * @ngdoc directive
   * @name pascalprecht.translate.directive:translate
   * @requires $compile
   * @requires $filter
   * @requires $interpolate
   * @restrict A
   *
   * @description
   * Translates given translation id either through attribute or DOM content.
   * Internally it uses `translate` filter to translate translation id. It possible to
   * pass an optional `translate-values` object literal as string into translation id.
   *
   * @param {string=} translate Translation id which could be either string or interpolated string.
   * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
   * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
   * @param {string=} translate-default will be used unless translation was successful
   * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translate#usePostCompiling}
   *
   * @example
     <example module="ngView">
      <file name="index.html">
        <div ng-controller="TranslateCtrl">
  
          <pre translate="TRANSLATION_ID"></pre>
          <pre translate>TRANSLATION_ID</pre>
          <pre translate translate-attr-title="TRANSLATION_ID"></pre>
          <pre translate="{{translationId}}"></pre>
          <pre translate>{{translationId}}</pre>
          <pre translate="WITH_VALUES" translate-values="{value: 5}"></pre>
          <pre translate translate-values="{value: 5}">WITH_VALUES</pre>
          <pre translate="WITH_VALUES" translate-values="{{values}}"></pre>
          <pre translate translate-values="{{values}}">WITH_VALUES</pre>
          <pre translate translate-attr-title="WITH_VALUES" translate-values="{{values}}"></pre>
  
        </div>
      </file>
      <file name="script.js">
        angular.module('ngView', ['pascalprecht.translate'])
  
        .config(function ($translateProvider) {
  
          $translateProvider.translations('en',{
            'TRANSLATION_ID': 'Hello there!',
            'WITH_VALUES': 'The following value is dynamic: {{value}}'
          }).preferredLanguage('en');
  
        });
  
        angular.module('ngView').controller('TranslateCtrl', function ($scope) {
          $scope.translationId = 'TRANSLATION_ID';
  
          $scope.values = {
            value: 78
          };
        });
      </file>
      <file name="scenario.js">
        it('should translate', function () {
          inject(function ($rootScope, $compile) {
            $rootScope.translationId = 'TRANSLATION_ID';
  
            element = $compile('<p translate="TRANSLATION_ID"></p>')($rootScope);
            $rootScope.$digest();
            expect(element.text()).toBe('Hello there!');
  
            element = $compile('<p translate="{{translationId}}"></p>')($rootScope);
            $rootScope.$digest();
            expect(element.text()).toBe('Hello there!');
  
            element = $compile('<p translate>TRANSLATION_ID</p>')($rootScope);
            $rootScope.$digest();
            expect(element.text()).toBe('Hello there!');
  
            element = $compile('<p translate>{{translationId}}</p>')($rootScope);
            $rootScope.$digest();
            expect(element.text()).toBe('Hello there!');
  
            element = $compile('<p translate translate-attr-title="TRANSLATION_ID"></p>')($rootScope);
            $rootScope.$digest();
            expect(element.attr('title')).toBe('Hello there!');
          });
        });
      </file>
     </example>
   */
  .directive('translate', ['$translate', '$q', '$interpolate', '$compile', '$parse', '$rootScope', function ($translate, $q, $interpolate, $compile, $parse, $rootScope) {
  
    return {
      restrict: 'AE',
      scope: true,
      compile: function (tElement, tAttr) {
  
        var translateValuesExist = (tAttr.translateValues) ?
          tAttr.translateValues : undefined;
  
        var translateInterpolation = (tAttr.translateInterpolation) ?
          tAttr.translateInterpolation : undefined;
  
        var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i);
  
        var interpolateRegExp = "^(.*)(" + $interpolate.startSymbol() + ".*" + $interpolate.endSymbol() + ")(.*)",
            watcherRegExp = "^(.*)" + $interpolate.startSymbol() + "(.*)" + $interpolate.endSymbol() + "(.*)";
  
        return function linkFn(scope, iElement, iAttr) {
  
          scope.interpolateParams = {};
          scope.preText = "";
          scope.postText = "";
          var translationIds = {};
  
          // Ensures any change of the attribute "translate" containing the id will
          // be re-stored to the scope's "translationId".
          // If the attribute has no content, the element's text value (white spaces trimmed off) will be used.
          var observeElementTranslation = function (translationId) {
            if (angular.equals(translationId , '') || !angular.isDefined(translationId)) {
              // Resolve translation id by inner html if required
              var interpolateMatches = iElement.text().match(interpolateRegExp);
              // Interpolate translation id if required
              if (angular.isArray(interpolateMatches)) {
                scope.preText = interpolateMatches[1];
                scope.postText = interpolateMatches[3];
                translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent);
                watcherMatches = iElement.text().match(watcherRegExp);
                if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) {
                  scope.$watch(watcherMatches[2], function (newValue) {
                    translationIds.translate = newValue;
                    updateTranslations();
                  });
                }
              } else {
                translationIds.translate = iElement.text().replace(/^\s+|\s+$/g,'');
              }
            } else {
              translationIds.translate = translationId;
            }
            updateTranslations();
          };
  
          var observeAttributeTranslation = function (translateAttr) {
            iAttr.$observe(translateAttr, function (translationId) {
              translationIds[translateAttr] = translationId;
              updateTranslations();
            });
          };
  
          iAttr.$observe('translate', function (translationId) {
            observeElementTranslation(translationId);
          });
  
          for (var translateAttr in iAttr) {
            if(iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr') {
              observeAttributeTranslation(translateAttr);
            }
          }
  
          iAttr.$observe('translateDefault', function (value) {
            scope.defaultText = value;
          });
  
          if (translateValuesExist) {
            iAttr.$observe('translateValues', function (interpolateParams) {
              if (interpolateParams) {
                scope.$parent.$watch(function () {
                  angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent));
                });
              }
            });
          }
  
          if (translateValueExist) {
            var observeValueAttribute = function (attrName) {
              iAttr.$observe(attrName, function (value) {
                var attributeName = angular.lowercase(attrName.substr(14, 1)) + attrName.substr(15);
                scope.interpolateParams[attributeName] = value;
              });
            };
            for (var attr in iAttr) {
              if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') {
                observeValueAttribute(attr);
              }
            }
          }
  
          // Master update function
          var updateTranslations = function () {
            for (var key in translationIds) {
              if (translationIds.hasOwnProperty(key) && translationIds[key]) {
                updateTranslation(key, translationIds[key], scope, scope.interpolateParams);
              }
            }
          };
  
          // Put translation processing function outside loop
          var updateTranslation = function(translateAttr, translationId, scope, interpolateParams) {
            $translate(translationId, interpolateParams, translateInterpolation)
              .then(function (translation) {
                applyTranslation(translation, scope, true, translateAttr);
              }, function (translationId) {
                applyTranslation(translationId, scope, false, translateAttr);
              });
          };
  
          var applyTranslation = function (value, scope, successful, translateAttr) {
            if (translateAttr === 'translate') {
              // default translate into innerHTML
              if (!successful && typeof scope.defaultText !== 'undefined') {
                value = scope.defaultText;
              }
              iElement.html(scope.preText + value + scope.postText);
              var globallyEnabled = $translate.isPostCompilingEnabled();
              var locallyDefined = typeof tAttr.translateCompile !== 'undefined';
              var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false';
              if ((globallyEnabled && !locallyDefined) || locallyEnabled) {
                $compile(iElement.contents())(scope);
              }
            } else {
              // translate attribute
              if (!successful && typeof scope.defaultText !== 'undefined') {
                value = scope.defaultText;
              }
              var attributeName = iAttr.$attr[translateAttr].substr(15);
              iElement.attr(attributeName, value);
            }
          };
  
          scope.$watch('interpolateParams', updateTranslations, true);
  
          // Ensures the text will be refreshed after the current language was changed
          // w/ $translate.use(...)
          var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations);
  
          // ensure translation will be looked up at least one
          if (iElement.text().length) {
            observeElementTranslation('');
          }
          updateTranslations();
          scope.$on('$destroy', unbind);
        };
      }
    };
  }]);
  
  angular.module('pascalprecht.translate')
  /**
   * @ngdoc directive
   * @name pascalprecht.translate.directive:translateCloak
   * @requires $rootScope
   * @requires $translate
   * @restrict A
   *
   * $description
   * Adds a `translate-cloak` class name to the given element where this directive
   * is applied initially and removes it, once a loader has finished loading.
   *
   * This directive can be used to prevent initial flickering when loading translation
   * data asynchronously.
   *
   * The class name is defined in
   * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}.
   *
   * @param {string=} translate-cloak If a translationId is provided, it will be used for showing
   *                                  or hiding the cloak. Basically it relies on the translation
   *                                  resolve.
   */
  .directive('translateCloak', ['$rootScope', '$translate', function ($rootScope, $translate) {
  
    return {
      compile: function (tElement) {
        var applyCloak = function () {
          tElement.addClass($translate.cloakClassName());
        },
        removeCloak = function () {
          tElement.removeClass($translate.cloakClassName());
        },
        removeListener = $rootScope.$on('$translateChangeEnd', function () {
          removeCloak();
          removeListener();
          removeListener = null;
        });
        applyCloak();
  
        return function linkFn(scope, iElement, iAttr) {
          // Register a watcher for the defined translation allowing a fine tuned cloak
          if (iAttr.translateCloak && iAttr.translateCloak.length) {
            iAttr.$observe('translateCloak', function (translationId) {
              $translate(translationId).then(removeCloak, applyCloak);
            });
          }
        };
      }
    };
  }]);
  
  angular.module('pascalprecht.translate')
  /**
   * @ngdoc filter
   * @name pascalprecht.translate.filter:translate
   * @requires $parse
   * @requires pascalprecht.translate.$translate
   * @function
   *
   * @description
   * Uses `$translate` service to translate contents. Accepts interpolate parameters
   * to pass dynamized values though translation.
   *
   * @param {string} translationId A translation id to be translated.
   * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation.
   *
   * @returns {string} Translated text.
   *
   * @example
     <example module="ngView">
      <file name="index.html">
        <div ng-controller="TranslateCtrl">
  
          <pre>{{ 'TRANSLATION_ID' | translate }}</pre>
          <pre>{{ translationId | translate }}</pre>
          <pre>{{ 'WITH_VALUES' | translate:'{value: 5}' }}</pre>
          <pre>{{ 'WITH_VALUES' | translate:values }}</pre>
  
        </div>
      </file>
      <file name="script.js">
        angular.module('ngView', ['pascalprecht.translate'])
  
        .config(function ($translateProvider) {
  
          $translateProvider.translations('en', {
            'TRANSLATION_ID': 'Hello there!',
            'WITH_VALUES': 'The following value is dynamic: {{value}}'
          });
          $translateProvider.preferredLanguage('en');
  
        });
  
        angular.module('ngView').controller('TranslateCtrl', function ($scope) {
          $scope.translationId = 'TRANSLATION_ID';
  
          $scope.values = {
            value: 78
          };
        });
      </file>
     </example>
   */
  .filter('translate', ['$parse', '$translate', function ($parse, $translate) {
    var translateFilter = function (translationId, interpolateParams, interpolation) {
  
      if (!angular.isObject(interpolateParams)) {
        interpolateParams = $parse(interpolateParams)(this);
      }
  
      return $translate.instant(translationId, interpolateParams, interpolation);
    };
  
    // Since AngularJS 1.3, filters which are not stateless (depending at the scope)
    // have to explicit define this behavior.
    translateFilter.$stateful = true;
  
    return translateFilter;
  }]);
  
  /**
   * @license AngularJS v1.3.0-beta.2
   * (c) 2010-2014 Google, Inc. http://angularjs.org
   * License: MIT
   */
  (function(window, angular, undefined) {'use strict';
  
  /**
   * @ngdoc module
   * @name ngCookies
   * @description
   *
   * # ngCookies
   *
   * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
   *
   *
   * <div doc-module-components="ngCookies"></div>
   *
   * See {@link ngCookies.$cookies `$cookies`} and
   * {@link ngCookies.$cookieStore `$cookieStore`} for usage.
   */
  
  
  angular.module('ngCookies', ['ng']).
    /**
     * @ngdoc service
     * @name $cookies
     *
     * @description
     * Provides read/write access to browser's cookies.
     *
     * Only a simple Object is exposed and by adding or removing properties to/from
     * this object, new cookies are created/deleted at the end of current $eval.
     *
     * Requires the {@link ngCookies `ngCookies`} module to be installed.
     *
     * @example
     <example>
       <file name="index.html">
         <script>
           function ExampleController($cookies) {
             // Retrieving a cookie
             var favoriteCookie = $cookies.myFavorite;
             // Setting a cookie
             $cookies.myFavorite = 'oatmeal';
           }
         </script>
       </file>
     </example>
     */
     factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
        var cookies = {},
            lastCookies = {},
            lastBrowserCookies,
            runEval = false,
            copy = angular.copy,
            isUndefined = angular.isUndefined;
  
        //creates a poller fn that copies all cookies from the $browser to service & inits the service
        $browser.addPollFn(function() {
          var currentCookies = $browser.cookies();
          if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
            lastBrowserCookies = currentCookies;
            copy(currentCookies, lastCookies);
            copy(currentCookies, cookies);
            if (runEval) $rootScope.$apply();
          }
        })();
  
        runEval = true;
  
        //at the end of each eval, push cookies
        //TODO: this should happen before the "delayed" watches fire, because if some cookies are not
        //      strings or browser refuses to store some cookies, we update the model in the push fn.
        $rootScope.$watch(push);
  
        return cookies;
  
  
        /**
         * Pushes all the cookies from the service to the browser and verifies if all cookies were
         * stored.
         */
        function push() {
          var name,
              value,
              browserCookies,
              updated;
  
          //delete any cookies deleted in $cookies
          for (name in lastCookies) {
            if (isUndefined(cookies[name])) {
              $browser.cookies(name, undefined);
            }
          }
  
          //update all cookies updated in $cookies
          for(name in cookies) {
            value = cookies[name];
            if (!angular.isString(value)) {
              if (angular.isDefined(lastCookies[name])) {
                cookies[name] = lastCookies[name];
              } else {
                delete cookies[name];
              }
            } else if (value !== lastCookies[name]) {
              $browser.cookies(name, value);
              updated = true;
            }
          }
  
          //verify what was actually stored
          if (updated){
            updated = false;
            browserCookies = $browser.cookies();
  
            for (name in cookies) {
              if (cookies[name] !== browserCookies[name]) {
                //delete or reset all cookies that the browser dropped from $cookies
                if (isUndefined(browserCookies[name])) {
                  delete cookies[name];
                } else {
                  cookies[name] = browserCookies[name];
                }
                updated = true;
              }
            }
          }
        }
      }]).
  
  
    /**
     * @ngdoc service
     * @name $cookieStore
     * @requires $cookies
     *
     * @description
     * Provides a key-value (string-object) storage, that is backed by session cookies.
     * Objects put or retrieved from this storage are automatically serialized or
     * deserialized by angular's toJson/fromJson.
     *
     * Requires the {@link ngCookies `ngCookies`} module to be installed.
     *
     * @example
     */
     factory('$cookieStore', ['$cookies', function($cookies) {
  
        return {
          /**
           * @ngdoc method
           * @name $cookieStore#get
           *
           * @description
           * Returns the value of given cookie key
           *
           * @param {string} key Id to use for lookup.
           * @returns {Object} Deserialized cookie value.
           */
          get: function(key) {
            var value = $cookies[key];
            return value ? angular.fromJson(value) : value;
          },
  
          /**
           * @ngdoc method
           * @name $cookieStore#put
           *
           * @description
           * Sets a value for given cookie key
           *
           * @param {string} key Id for the `value`.
           * @param {Object} value Value to be stored.
           */
          put: function(key, value) {
            $cookies[key] = angular.toJson(value);
          },
  
          /**
           * @ngdoc method
           * @name $cookieStore#remove
           *
           * @description
           * Remove given cookie
           *
           * @param {string} key Id of the key-value pair to delete.
           */
          remove: function(key) {
            delete $cookies[key];
          }
        };
  
      }]);
  
  
  })(window, window.angular);
  
  'use strict'
  
  angular.module('spBlogger',['ngResource','ui.router','ngAnimate','pascalprecht.translate','ngCookies','spBlogger.controllers','spBlogger.posts','spBlogger.admin','spBlogger.directives','spBlogger.filters','spBlogger.services']);
  
  angular.module('spBlogger').value('version','V1.0');  // value
  
  
  angular.module('spBlogger').config(['$translateProvider',function($translateProvider){
  $translateProvider.translations('en', {
  TITLE: 'The Single Page Blogger',
  SUBTITLE: 'One Stop Blogging Solution',
  COMMENTS: 'Comments',
  BY:'By',
  ADD:'Add'
  });
  $translateProvider.translations('de', {
  TITLE: 'Single Page Blogger',
  SUBTITLE: 'Die Komplettlösung für Ihr Blog',
  COMMENTS: 'Kommentare',
  BY:'Von',
  ADD:'Hinzufügen'
  });
  $translateProvider.preferredLanguage('en');
  
  //$httpProvider.defaults.withCredentials = true;
  
  }]);
  
  angular.module('spBlogger').config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.withCredentials = true;
  }])
  
  
  angular.module('spBlogger').run(['$state','$rootScope','$translate',function($state,$rootScope,$translate){
  		$state.go('allPosts');  // first state on entry to the site  // run is run when all modules are loaded
  	
  		$rootScope.languagePreference={currentLanguage:'en'};
  		$rootScope.languagePreference.switchLanguage=function(key){
  		$translate.use(key);
  		$rootScope.languagePreference.currentLanguage=key;
  		}
  	
  	}]); 
  
  'use strict'
  
  angular.module('spBlogger.controllers',[]);
  'use strict'
  
  angular.module('spBlogger.directives',[]);
  
  angular.module('spBlogger.directives').directive('appVersion',function(version){
  	return {
  		restrict: 'AE',
  		link: function(scope,elem,attrs){
  			elem.html(version);
  		}
  	}	
  });
  'use strict'
  
  angular.module('spBlogger.filters',[]);
  'use strict'
  
  angular.module('spBlogger.services',[]);
  angular.module('spBlogger.admin',['spBlogger.admin.controllers','spBlogger.admin.services','spBlogger.admin.filters']); 
  
  angular.module('spBlogger.admin').config(['$stateProvider',function($stateProvider){
  	$stateProvider.state('admin',{ // route definition object
  		url:'/admin',
  		abstract:true,  // cant be loaded directly, only loaded when one of the children are loaded
  		controller:'AdminController',
  		  resolve:{  // has to be resolved for controller to fire
              user:['authService','$q',function(authService,$q){
                  return authService.user || $q.reject({unAuthorized:true});
              }]
          },
  		templateUrl:'modules/admin/views/admin-home.html'
  	}).state('admin.postNew',{
  		url:'/posts/new',
  		controller: 'PostCreationController',
  		templateUrl: 'modules/admin/views/admin-new-post.html'
  	}).state('admin.postUpdate',{
  		url:'/posts/:id/edit',
  		controller: 'PostUpdateController',
  		templateUrl: 'modules/admin/views/admin-update-post.html'
  	}).state('admin.postViewAll',{
  		url:'',  // default state
  		controller: 'PostListController',
  		templateUrl: 'modules/admin/views/admin-all-posts.html'
  	}).state('login',{
  		url:'/login',
  		controller:'LoginController', resolve:{
              user:['authService','$q',function(authService,$q){
                  if(authService.user){
                      return $q.reject({authorized:true});
                  }
              }]
          },
  		templateUrl:'modules/admin/views/login.html'
  	});
  }]).run(['$rootScope','$state','$cookieStore','authService',function($rootScope,$state,$cookieStore,authService){
  
      $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
  
          if(error.unAuthorized) {
              $state.go('login');
          }
          else if(error.authorized){
              $state.go('admin.postViewAll');
          }
      });
  
      authService.user=$cookieStore.get('user');
  
  }]);;
  
  'use strict'
  
  angular.module('spBlogger.admin.controllers',[]).controller('PostCreationController',['$scope','$state','Post','permalinkFilter', function($scope,$state,Post,permalinkFilter){
  	$scope.post=new Post(); // Create an empty Post instance
  	
  	$scope.buttonText="Create"; // Set initial label for button
  	
  	$scope.savePost=function(){
  		$scope.buttonText="Saving..."; // Once clicked change button text
  		//$scope.post.permalink=angular.lowercase($scope.post.title).replace(/[\s]/g,'-');  // generate permalink
  		$scope.post.permalink = permalinkFilter($scope.post.title); // create the permalink with the permalink filter
  		$scope.post.$save(function(){
  			$state.go('admin.postViewAll'); // Once saved go to state admin.postViewAll
  		});
  	}
  }]);
  
  angular.module('spBlogger.admin.controllers').controller('PostUpdateController',['$scope','Post','$stateParams','$state',function($scope,Post,$stateParams,$state){
  	//alert($stateParams.id);
  	$scope.post=Post.get({id:$stateParams.id}); // Obtain the Post from backend. Search by Id
  	$scope.buttonText="Update";  // Set initial label for button
  	$scope.updatePost=function(){
  		$scope.buttonText="Updating..."; // Once clicked change button text
  		$scope.post.$update(function(){
  			$state.go('admin.postViewAll');  // Once updated go to state 'admin.postViewAll
  		});
  	}
  }]);
  
  angular.module('spBlogger.admin.controllers').controller('PostListController',['$scope','Post','popupService','$state',function($scope,Post,popupService,$state){
  	$scope.posts=Post.query();  // Obtain all the posts from backend
  	$scope.deletePost=function(post){
  		if(popupService.showPopup('Really delete this?')){  // Ask for confirmation
  			post.$delete(function(){
  				$state.go('admin.postViewAll',undefined,{  // once deleted reload the state
  					reload:true
  				});
  			});
  		}
  	}
  }]).controller('LoginController',['$scope','authService','$state',function($scope,authService,$state){
  
      $scope.buttonText="Login";
  
      $scope.login=function(){
  
          $scope.buttonText="Logging in. . .";
  
          authService.login($scope.credentials.username,$scope.credentials.password).then(function(data){
              $state.go('admin.postViewAll');
          },function(err){
              $scope.invalidLogin=true;
          }).finally(function(){
              $scope.buttonText="Login";
          });
      }
  }]).controller('AdminController',['$scope','authService','$state','user',function($scope,authService,$state,user){
  
      $scope.logout=function(){
          authService.logout().then(function(){
              $state.go('login');
          });
      }
  }]);
  
  // added this?
  
  //angular.module('spBlogger.admin.controllers').controller('AdminController',['$scope',function($scope){
  	
  //}]);
  
  //angular.module('spBlogger.admin.controllers').controller('LoginController',['$scope','authService','$state',function($scope,authService,$state){
  //$scope.buttonText="Login";
  //$scope.login=function(){
  //$scope.buttonText="Logging in. . .";
  //authService.login($scope.credentials.username,$scope.credentials.password).then(function(data){
  //$state.go('admin.postViewAll');
  //},function(err){
  //$scope.invalidLogin=true;
  //}).finally(function(){
  //$scope.buttonText="Login";
  //});
  //}
  //}]);
  
  
  //angular.module('spBlogger.admin.controllers',[]).controller('AdminController',['$scope',function($scope){
  	
  //}]).controller('PostUpdateController',['$scope',function($scope){
  	
  //}]).controller('PostListController',['$scope',function($scope){
  	
  //}]).controller('PostCreationController',['$scope',function($scope){ // added this controller
  	
  //}]);
  // the first controller is part of the declaration of the module. the next call the module and add more controllers to it
  
  'use strict'
  
  angular.module('spBlogger.admin.services', []).factory('Post',['$resource','API_ENDPOINT',function($resource,API_ENDPOINT){
      return $resource(API_ENDPOINT, { id: '@_id' }, { // use $resource service for simplified $http to rest server CRUD operations 
          update: {
              method: 'PUT'
          }
      });
  }]).service('popupService',['$window',function($window){
      this.showPopup=function(message){
          return $window.confirm(message); //Ask the users if they really want to delete the post entry
      }
  }]).factory('authService',['AUTH_ENDPOINT','LOGOUT_ENDPOINT','$http','$cookieStore',function(AUTH_ENDPOINT,LOGOUT_ENDPOINT,$http,$cookieStore){
  
      var auth={};
  
      auth.login=function(username,password){
          return $http.post(AUTH_ENDPOINT,{username:username,password:password}).then(function(response,status){
              auth.user=response.data; console.log(auth.user);
              $cookieStore.put('user',auth.user);
              return auth.user;
          });
      }
  
      auth.logout=function(){
          return $http.post(LOGOUT_ENDPOINT).then(function(response){
              auth.user=undefined;
              $cookieStore.remove('user');
          });
      }
  
      return auth;
  
  }]);
  
  
  angular.module('spBlogger.admin.services').value('API_ENDPOINT','http://localhost:8080/api/posts/:id'); // This is our end point
  angular.module('spBlogger.admin.services').value('AUTH_ENDPOINT','http://localhost:8080/login');
  angular.module('spBlogger.admin.services').value('LOGOUT_ENDPOINT','http://localhost:8080/logout');
  
  
  
  
  
  
  
  
  
  
  
  
  'use strict'
  
  angular.module('spBlogger.admin.filters',[]).filter('permalink',function(){
      return function(title){
          return title===undefined ? '' : angular.lowercase(title).replace(/[\s]/g,'-');
      }
  });
  
  angular.module('spBlogger.admin.filters').filter('wordcount',function(){
      return function(input){
          return input===undefined ? 0 : input.split(/\s/g).length; console.log(input);
      }
  });
  
  'use strict'
  
  angular.module('spBlogger.posts',['spBlogger.posts.controllers','spBlogger.posts.services','spBlogger.posts.directives']);
  
  //angular.module('spBlogger.posts',['spBlogger.posts.controllers','spBlogger.posts.directives','spBlogger.posts.services','spBlogger.posts.filters']);
  
  angular.module('spBlogger.posts').config(['$stateProvider','$locationProvider',function($stateProvider,$locationProvider){
  	$stateProvider.state('allPosts',{
  		url:'/posts',
  		templateUrl:'modules/posts/views/posts.html',
  		controller: 'PostController'
  	});
  	$stateProvider.state('singlePost',{
  		url:'/posts/:id/:permalink',
  		templateUrl: 'modules/posts/views/singlePost.html',
  		controller: 'PostDetailsController'
  	});
  }]);
  
  'use strict'
  
  // post service from the admin module.......
  angular.module('spBlogger.posts.controllers',[]).controller('PostController',['$scope','Post',function($scope,Post){
  
  	$scope.posts=Post.query();   // obtain all the posts
  
  }]).controller('PostDetailsController',['$stateParams','$state','$scope','Post',function($stateParams,$state,$scope,Post){
  
      $scope.closePost=function(){
          $state.go('allPosts');
      };
  		
  			//alert("we need an id here .. "+ $stateParams.id);
  			
      $scope.singlePost=Post.get({id:$stateParams.id}); // obtain a single post // Nb this post will also be passed to the directive
      //console.log($scope.singlePost);
  
  }]);
  
  //angular.module('spBlogger.posts.controllers',[]).controller
  //('PostController',['$scope','postService',
  //function($scope,postService){
  	//$scope.getAllPosts=function(){
  		//return postService.getAll();
  	//};
  	
  	//$scope.posts=$scope.getAllPosts();
  	
  //}]).controller('PostDetailsController',['$stateParams','$state',
  //'$scope','postService',function($stateParams,$state,$scope,postService){
  	//$scope.getPostById=function(id){
  		//return postService.getPostById(id);
  	//};
  	//$scope.closePost=function(){
  		//$state.go('allPosts');
  	//};
  	//$scope.singlePost=$scope.getPostById($stateParams.id);
  	
  //}]);
  
  'use strict'
  
  angular.module('spBlogger.posts.filters',[]);
  'use strict'
  
  angular.module('spBlogger.posts.directives',[]).directive('spbComments',function(Post){
  	return{
  		restrict:'AEC',
  		scope:{
  			postInstance:'='
  		},
  		replace:true,
  		link:function(scope,elem,attrs){
  			scope.saveComment=function(){
  				
  				var postID=scope.postInstance._id,
  				
  				savedPostInstance={};
  				
  				scope.comment.datePublished=new Date(); // Give a date to the comment
  				
  				angular.copy(scope.postInstance,savedPostInstance); // copy the post instance in 'scope' to a variable 'savedPostInstance'
  				
  				savedPostInstance.comments.unshift(scope.comment); // push the comment to the savedPostInstance
  				
  				scope.postInstance.comments.unshift(scope.comment); // pust the comment to the 'scope.postInstance' as well
  				
  				scope.comment={}; // clear the comment
  			
  				savedPostInstance.$update(); // Now update 'savedPostInstance' so that the new comment goes to the server
  				// updating would clear the object, which is why it is copied from postInstance, which is the object which is displayed
  
  				//console.log(scope.postInstance);
  			}
  		},
  		templateUrl:'modules/posts/views/comments.html'
  		
  	}
  });
  
  'use strict'
  
  angular.module('spBlogger.posts.services',[]).factory('postService',function(){
  	return {
  		//posts: [{
  			//id: 1,
  			//title: 'Simple title1',
  			//content: 'Sample content this is some sample content, this is some sample content, this is some sample content, this is some sample content, this is some sample content.',
  			//permalink: 'simple-title1',
  			//author: 'Koru',
  			//datePublished: '2012-04-04'
  			//}, {
  			//id: 2,
  			//title: 'Simple title2',
  			//content: 'Sample content...',
  			//permalink: 'simple-title2',
  			//author: 'Sandeep',
  			//datePublished: '2012-05-04'
  			//}, {
  			//id: 3,
  			//title: 'Simple title3',
  			//content: 'Sample content...',
  			//permalink: 'simple-title3',
  			//author: 'Samosa',
  			//datePublished: '2012-06-04'
  			//}, {
  			//id: 4,
  			//title: 'Simple title4',
  			//content: 'Sample content...',
  			//permalink: 'simple-title4',
  			//author: 'Sanibbets',
  			//datePublished: '2012-07-04'
  		//}],
  		getAll: function(){
  			return this.posts;
  		},
  		getPostById: function(id){
  			for(var i in this.posts){
  				if(this.posts[i].id == id){
  					return this.posts[i];
  				}
  			}
  		},
  	}
  });
  